From 70c220b00c0a75f6fa4d1f72caf1b8e8efb5e872 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 10:06:47 +0000 Subject: [PATCH 01/61] (chore): Bump eslint-plugin-simple-import-sort from 10.0.0 to 12.0.0 Bumps [eslint-plugin-simple-import-sort](https://github.com/lydell/eslint-plugin-simple-import-sort) from 10.0.0 to 12.0.0. - [Changelog](https://github.com/lydell/eslint-plugin-simple-import-sort/blob/main/CHANGELOG.md) - [Commits](https://github.com/lydell/eslint-plugin-simple-import-sort/compare/v10.0.0...v12.0.0) --- updated-dependencies: - dependency-name: eslint-plugin-simple-import-sort dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a6a6ebd6..6b1330ba4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,7 +71,7 @@ "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-simple-import-sort": "^10.0.0", + "eslint-plugin-simple-import-sort": "^12.0.0", "eslint-plugin-sort-keys-fix": "^1.1.2", "eslint-plugin-testing-library": "^6.2.0", "git-revision-webpack-plugin": "^5.0.0", @@ -9504,9 +9504,9 @@ } }, "node_modules/eslint-plugin-simple-import-sort": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz", - "integrity": "sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.0.0.tgz", + "integrity": "sha512-8o0dVEdAkYap0Cn5kNeklaKcT1nUsa3LITWEuFk3nJifOoD+5JQGoyDUW2W/iPWwBsNBJpyJS9y4je/BgxLcyQ==", "dev": true, "peerDependencies": { "eslint": ">=5.0.0" diff --git a/package.json b/package.json index f1c067436..5b755f6a5 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-simple-import-sort": "^10.0.0", + "eslint-plugin-simple-import-sort": "^12.0.0", "eslint-plugin-sort-keys-fix": "^1.1.2", "eslint-plugin-testing-library": "^6.2.0", "git-revision-webpack-plugin": "^5.0.0", From 23cb5c38885878e73919489daebdc6032e608ab9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 10:07:28 +0000 Subject: [PATCH 02/61] (chore): Bump @redhat-cloud-services/frontend-components-utilities Bumps [@redhat-cloud-services/frontend-components-utilities](https://github.com/RedHatInsights/frontend-components) from 4.0.2 to 4.0.4. - [Commits](https://github.com/RedHatInsights/frontend-components/commits) --- updated-dependencies: - dependency-name: "@redhat-cloud-services/frontend-components-utilities" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a6a6ebd6..a66801961 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@redhat-cloud-services/frontend-components": "^4.2.3", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", - "@redhat-cloud-services/frontend-components-utilities": "^4.0.2", + "@redhat-cloud-services/frontend-components-utilities": "^4.0.4", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.1.0", "@unleash/proxy-client-react": "^4.1.2", @@ -2894,9 +2894,9 @@ "peer": true }, "node_modules/@redhat-cloud-services/frontend-components-utilities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-4.0.2.tgz", - "integrity": "sha512-LUAaJwpi8EmyrNrGum53HcSpO0rrwvXkdEmaXjfooRlvVtLz8twsjaiM2jFfqWXbZMq43gQufn/wy8nquRoq6w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-4.0.4.tgz", + "integrity": "sha512-46bIIwp64LZ401dj3ZznQ3ENuGcanwjqgtyH4ft1jlY7Vo9I/Lc3s+xrl1xpfSRUuqN51JkDpfoojRtUeXULnQ==", "dependencies": { "@redhat-cloud-services/types": "^0.0.24", "@sentry/browser": "^5.30.0", diff --git a/package.json b/package.json index f1c067436..c6393c4f0 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@redhat-cloud-services/frontend-components": "^4.2.3", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", - "@redhat-cloud-services/frontend-components-utilities": "^4.0.2", + "@redhat-cloud-services/frontend-components-utilities": "^4.0.4", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.1.0", "@unleash/proxy-client-react": "^4.1.2", From 9229b8110af5c35a294d206ff22b139a05d4bb0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 10:09:51 +0000 Subject: [PATCH 03/61] (chore): Bump @types/react-dom from 18.2.18 to 18.2.19 Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 18.2.18 to 18.2.19. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a6a6ebd6..2461dbde5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ "@types/jest": "^29.5.12", "@types/qs": "^6.9.11", "@types/react": "^18.2.53", - "@types/react-dom": "^18.2.18", + "@types/react-dom": "^18.2.19", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^6.21.0", @@ -4074,9 +4074,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.18", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", - "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz", + "integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==", "dev": true, "dependencies": { "@types/react": "*" diff --git a/package.json b/package.json index f1c067436..51ef61c01 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "@types/jest": "^29.5.12", "@types/qs": "^6.9.11", "@types/react": "^18.2.53", - "@types/react-dom": "^18.2.18", + "@types/react-dom": "^18.2.19", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^6.21.0", From ad7ab26f1c094ee83c4c4efaaacb37d662cb96cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:17:56 +0000 Subject: [PATCH 04/61] (chore): Bump eslint-plugin-jsdoc from 48.0.4 to 48.1.0 Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 48.0.4 to 48.1.0. - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v48.0.4...v48.1.0) --- updated-dependencies: - dependency-name: eslint-plugin-jsdoc dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 18 +++++++++--------- package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a6a6ebd6..7a428362c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "eslint": "^8.56.0", "eslint-plugin-formatjs": "^4.12.2", "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-jsdoc": "^48.0.4", + "eslint-plugin-jsdoc": "^48.1.0", "eslint-plugin-markdown": "^3.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", @@ -742,9 +742,9 @@ "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==" }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.41.0.tgz", - "integrity": "sha512-aKUhyn1QI5Ksbqcr3fFJj16p99QdjUxXAEuFst1Z47DRyoiMwivIH9MV/ARcJOCXVjPfjITciej8ZD2O/6qUmw==", + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.42.0.tgz", + "integrity": "sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==", "dev": true, "dependencies": { "comment-parser": "1.4.1", @@ -9011,19 +9011,19 @@ "dev": true }, "node_modules/eslint-plugin-jsdoc": { - "version": "48.0.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.0.4.tgz", - "integrity": "sha512-A0cH+5svWPXzGZszBjXA1t0aAqVGS+/x3i02KFmb73rU0iMLnadEcVWcD/dGBZHIfAMKr3YpWh58f6wn4N909w==", + "version": "48.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.1.0.tgz", + "integrity": "sha512-g9S8ukmTd1DVcV/xeBYPPXOZ6rc8WJ4yi0+MVxJ1jBOrz5kmxV9gJJQ64ltCqIWFnBChLIhLVx3tbTSarqVyFA==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.41.0", + "@es-joy/jsdoccomment": "~0.42.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", "is-builtin-module": "^3.2.1", - "semver": "^7.5.4", + "semver": "^7.6.0", "spdx-expression-parse": "^4.0.0" }, "engines": { diff --git a/package.json b/package.json index f1c067436..109576749 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "eslint": "^8.56.0", "eslint-plugin-formatjs": "^4.12.2", "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-jsdoc": "^48.0.4", + "eslint-plugin-jsdoc": "^48.1.0", "eslint-plugin-markdown": "^3.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", From 920219975c1fb1b672ea9aab19b0c218b210ec39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:20:24 +0000 Subject: [PATCH 05/61] (chore): Bump @reduxjs/toolkit from 2.1.0 to 2.2.1 Bumps [@reduxjs/toolkit](https://github.com/reduxjs/redux-toolkit) from 2.1.0 to 2.2.1. - [Release notes](https://github.com/reduxjs/redux-toolkit/releases) - [Commits](https://github.com/reduxjs/redux-toolkit/compare/v2.1.0...v2.2.1) --- updated-dependencies: - dependency-name: "@reduxjs/toolkit" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a6a6ebd6..a17b991aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@redhat-cloud-services/frontend-components-translations": "^3.2.7", "@redhat-cloud-services/frontend-components-utilities": "^4.0.2", "@redhat-cloud-services/rbac-client": "^1.2.13", - "@reduxjs/toolkit": "^2.1.0", + "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.1.2", "axios": "^1.6.7", "date-fns": "^3.3.1", @@ -2983,9 +2983,9 @@ "integrity": "sha512-P50stc+mnWLycID46/AKmD/760r5N1eoam//O6MUVriqVorUdht7xkUL78aJZU1vw8WW6xlrDHwz3F6BM148qg==" }, "node_modules/@reduxjs/toolkit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.1.0.tgz", - "integrity": "sha512-nfJ/b4ZhzUevQ1ZPKjlDL6CMYxO4o7ZL7OSsvSOxzT/EN11LsBDgTqP7aedHtBrFSVoK7oTP1SbMWUwGb30NLg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.1.tgz", + "integrity": "sha512-8CREoqJovQW/5I4yvvijm/emUiCCmcs4Ev4XPWd4mizSO+dD3g5G6w34QK5AGeNrSH7qM8Fl66j4vuV7dpOdkw==", "dependencies": { "immer": "^10.0.3", "redux": "^5.0.1", diff --git a/package.json b/package.json index f1c067436..642d120b1 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "@redhat-cloud-services/frontend-components-translations": "^3.2.7", "@redhat-cloud-services/frontend-components-utilities": "^4.0.2", "@redhat-cloud-services/rbac-client": "^1.2.13", - "@reduxjs/toolkit": "^2.1.0", + "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.1.2", "axios": "^1.6.7", "date-fns": "^3.3.1", From 47d4538cf0c16e3214a426d854a548d053dd549f Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Tue, 20 Feb 2024 08:31:30 -0500 Subject: [PATCH 06/61] Dependency updates --- package-lock.json | 461 +++++++++++++++++++++++++++++++++++++++++----- package.json | 9 +- 2 files changed, 424 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 66fecf818..ea9fa4cc5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "react-dom": "^18.2.0", "react-intl": "^6.6.2", "react-redux": "^9.1.0", - "react-router-dom": "^6.22.0", + "react-router-dom": "^6.22.1", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", @@ -55,12 +55,12 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/qs": "^6.9.11", - "@types/react": "^18.2.53", + "@types/react": "^18.2.57", "@types/react-dom": "^18.2.19", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.0.2", + "@typescript-eslint/parser": "^7.0.2", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", "eslint": "^8.56.0", @@ -3006,9 +3006,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.0.tgz", - "integrity": "sha512-HOil5aFtme37dVQTB6M34G95kPM3MMuqSmIRVCC52eKV+Y/tGSqw9P3rWhlAx6A+mz+MoX+XxsGsNJbaI5qCgQ==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.1.tgz", + "integrity": "sha512-zcU0gM3z+3iqj8UX45AmWY810l3oUmXM7uH4dt5xtzvMhRtYVhKGOmgOd1877dOPPepfCjUv57w+syamWIYe7w==", "engines": { "node": ">=14.0.0" } @@ -4064,9 +4064,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.53", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.53.tgz", - "integrity": "sha512-52IHsMDT8qATp9B9zoOyobW8W3/0QhaJQTw1HwRj0UY2yBpCAQ7+S/CqHYQ8niAm3p4ji+rWUQ9UCib0GxQ60w==", + "version": "18.2.57", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.57.tgz", + "integrity": "sha512-ZvQsktJgSYrQiMirAN60y4O/LRevIV8hUzSOSNB6gfR3/o3wCBFQx3sPwIYtuDMeiVgsSS3UzCV26tEzgnfvQw==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4305,16 +4305,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz", + "integrity": "sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/type-utils": "7.0.2", + "@typescript-eslint/utils": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -4330,8 +4330,66 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", + "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", + "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", + "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependenciesMeta": { "typescript": { @@ -4339,6 +4397,60 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz", + "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", + "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4373,15 +4485,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.2.tgz", + "integrity": "sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", "debug": "^4.3.4" }, "engines": { @@ -4392,7 +4504,65 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", + "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", + "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", + "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependenciesMeta": { "typescript": { @@ -4400,6 +4570,68 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", + "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/scope-manager": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", @@ -4418,13 +4650,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz", + "integrity": "sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/utils": "7.0.2", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -4436,7 +4668,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -4444,6 +4676,151 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", + "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", + "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", + "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/visitor-keys": "7.0.2", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz", + "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.0.2", + "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/typescript-estree": "7.0.2", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", + "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.0.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@typescript-eslint/types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", @@ -17624,11 +18001,11 @@ } }, "node_modules/react-router": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz", - "integrity": "sha512-q2yemJeg6gw/YixRlRnVx6IRJWZD6fonnfZhN1JIOhV2iJCPeRNSH3V1ISwHf+JWcESzLC3BOLD1T07tmO5dmg==", + "version": "6.22.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.1.tgz", + "integrity": "sha512-0pdoRGwLtemnJqn1K0XHUbnKiX0S4X8CgvVVmHGOWmofESj31msHo/1YiqcJWK7Wxfq2a4uvvtS01KAQyWK/CQ==", "dependencies": { - "@remix-run/router": "1.15.0" + "@remix-run/router": "1.15.1" }, "engines": { "node": ">=14.0.0" @@ -17638,12 +18015,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz", - "integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==", + "version": "6.22.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.1.tgz", + "integrity": "sha512-iwMyyyrbL7zkKY7MRjOVRy+TMnS/OPusaFVxM2P11x9dzSzGmLsebkCvYirGq0DWB9K9hOspHYYtDz33gE5Duw==", "dependencies": { - "@remix-run/router": "1.15.0", - "react-router": "6.22.0" + "@remix-run/router": "1.15.1", + "react-router": "6.22.1" }, "engines": { "node": ">=14.0.0" diff --git a/package.json b/package.json index 592f9efe6..bc0d0e72e 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "react-dom": "^18.2.0", "react-intl": "^6.6.2", "react-redux": "^9.1.0", - "react-router-dom": "^6.22.0", + "react-router-dom": "^6.22.1", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", @@ -95,12 +95,12 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/qs": "^6.9.11", - "@types/react": "^18.2.53", + "@types/react": "^18.2.57", "@types/react-dom": "^18.2.19", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.0.2", + "@typescript-eslint/parser": "^7.0.2", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", "eslint": "^8.56.0", @@ -131,6 +131,7 @@ "webpack-bundle-analyzer": "^4.10.1" }, "overrides": { + "@typescript-eslint/eslint-plugin": "^7.0.2", "redux": "^5.0.1" }, "insights": { From 7ae2d0c0642dda6bd73dd31f476baf85b29f6a8b Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Wed, 21 Feb 2024 08:05:07 -0500 Subject: [PATCH 07/61] Add dependabot group config --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 262506a14..8f7b312a9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,3 +14,14 @@ updates: pull-request-branch-name: separator: "_" open-pull-requests-limit: 15 + groups: + lint-dependencies: + patterns: + - "*eslint*" + test-dependencies: + patterns: + - "*jest*" + - "@testing-library*" + ci-dependencies: + patterns: + - "*" # update remaining deps in single PR From 9f62b81fab5f8f04fc55668cf5dec14d07623c02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:32:32 +0000 Subject: [PATCH 08/61] (chore): Bump the ci-dependencies group with 1 update Bumps the ci-dependencies group with 1 update: [@redhat-cloud-services/frontend-components-utilities](https://github.com/RedHatInsights/frontend-components). Updates `@redhat-cloud-services/frontend-components-utilities` from 4.0.4 to 4.0.5 - [Commits](https://github.com/RedHatInsights/frontend-components/commits) --- updated-dependencies: - dependency-name: "@redhat-cloud-services/frontend-components-utilities" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ci-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 21 +++++++++++---------- package.json | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index ea9fa4cc5..915db4d13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@redhat-cloud-services/frontend-components": "^4.2.3", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", - "@redhat-cloud-services/frontend-components-utilities": "^4.0.4", + "@redhat-cloud-services/frontend-components-utilities": "^4.0.5", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.1.2", @@ -2894,14 +2894,14 @@ "peer": true }, "node_modules/@redhat-cloud-services/frontend-components-utilities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-4.0.4.tgz", - "integrity": "sha512-46bIIwp64LZ401dj3ZznQ3ENuGcanwjqgtyH4ft1jlY7Vo9I/Lc3s+xrl1xpfSRUuqN51JkDpfoojRtUeXULnQ==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-4.0.5.tgz", + "integrity": "sha512-cwutgum9dWSROBrrV+ddMwi7VH+CxNV2LciqfdH/uyWXmUVGXPDifghMgkx4Th//VdS3+AVGSkinhIzNej9ArA==", "dependencies": { "@redhat-cloud-services/types": "^0.0.24", "@sentry/browser": "^5.30.0", "awesome-debounce-promise": "^2.1.0", - "axios": "^0.27.2", + "axios": "^0.28.0", "commander": "^2.20.3", "mkdirp": "^1.0.4", "react-content-loader": "^6.2.0" @@ -2918,12 +2918,13 @@ } }, "node_modules/@redhat-cloud-services/frontend-components-utilities/node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.28.0.tgz", + "integrity": "sha512-Tu7NYoGY4Yoc7I+Npf9HhUMtEEpV7ZiLH9yndTCoNhcpBH0kwcvFbzYN9/u5QKI5A6uefjsNNWaz5olJVYS62Q==", "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/@redhat-cloud-services/rbac-client": { diff --git a/package.json b/package.json index bc0d0e72e..a03338ed5 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@redhat-cloud-services/frontend-components": "^4.2.3", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", - "@redhat-cloud-services/frontend-components-utilities": "^4.0.4", + "@redhat-cloud-services/frontend-components-utilities": "^4.0.5", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.1.2", From 6a958770b246124236a6e283bda317f2029f87c1 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Wed, 21 Feb 2024 09:37:01 -0500 Subject: [PATCH 09/61] Add Unleash flag for tag mapping https://issues.redhat.com/browse/COST-3824 --- src/components/featureFlags/featureFlags.tsx | 4 +++- src/routes/settings/tagLabels/tagLabels.tsx | 8 +++++++- .../featureFlags/__snapshots__/featureFlags.test.ts.snap | 1 + src/store/featureFlags/featureFlagsActions.ts | 1 + src/store/featureFlags/featureFlagsReducer.ts | 3 +++ src/store/featureFlags/featureFlagsSelectors.ts | 2 ++ 6 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/featureFlags/featureFlags.tsx b/src/components/featureFlags/featureFlags.tsx index 6eefac1de..69af98cff 100644 --- a/src/components/featureFlags/featureFlags.tsx +++ b/src/components/featureFlags/featureFlags.tsx @@ -6,13 +6,14 @@ import { featureFlagsActions } from 'store/featureFlags'; // eslint-disable-next-line no-shadow export const enum FeatureToggle { - clusterInfo = 'cost-management.ui.cluster-info', // https://issues.redhat.com/browse/COST-4559 + clusterInfo = 'cost-management.ui.cluster.info', // https://issues.redhat.com/browse/COST-4559 exports = 'cost-management.ui.exports', // Async exports https://issues.redhat.com/browse/COST-2223 finsights = 'cost-management.ui.finsights', // RHEL support for FINsights https://issues.redhat.com/browse/COST-3306 ibm = 'cost-management.ui.ibm', // IBM https://issues.redhat.com/browse/COST-935 ros = 'cost-management.ui.ros', // ROS support https://issues.redhat.com/browse/COST-3477 rosBeta = 'cost-management.ui.ros-beta', // ROS support https://issues.redhat.com/browse/COST-3477 settingsPlatform = 'cost-management.ui.settings.platform', // Platform projects https://issues.redhat.com/browse/COST-3818 + tagMapping = 'cost-management.ui.tag.mapping', // https://issues.redhat.com/browse/COST-3824 } // The FeatureFlags component saves feature flags in store for places where Unleash hooks not available @@ -60,6 +61,7 @@ const useFeatureFlags = () => { isRosFeatureEnabled: client.isEnabled(FeatureToggle.ros) || (client.isEnabled(FeatureToggle.rosBeta) && isBeta()), // Need to check beta in prod isSettingsPlatformFeatureEnabled: client.isEnabled(FeatureToggle.settingsPlatform), + isTagMappingFeatureEnabled: client.isEnabled(FeatureToggle.tagMapping), }) ); }); diff --git a/src/routes/settings/tagLabels/tagLabels.tsx b/src/routes/settings/tagLabels/tagLabels.tsx index 9c118ef48..5e79f22ec 100644 --- a/src/routes/settings/tagLabels/tagLabels.tsx +++ b/src/routes/settings/tagLabels/tagLabels.tsx @@ -15,6 +15,7 @@ import { NotAvailable } from 'routes/components/page/notAvailable'; import * as queryUtils from 'routes/utils/query'; import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; +import { featureFlagsSelectors } from 'store/featureFlags'; import { settingsActions, settingsSelectors } from 'store/settings'; import { useStateCallback } from 'utils/hooks'; @@ -31,6 +32,7 @@ export interface TagLabelsMapProps { } export interface TagLabelsStateProps { + isTagMappingFeatureEnabled?: boolean; settings?: Settings; settingsError?: AxiosError; settingsStatus?: FetchStatus; @@ -54,7 +56,7 @@ const TagLabels: React.FC = ({ canWrite }) => { const dispatch: ThunkDispatch = useDispatch(); const intl = useIntl(); - const { settings, settingsError, settingsStatus } = useMapToProps({ query }); + const { isTagMappingFeatureEnabled, settings, settingsError, settingsStatus } = useMapToProps({ query }); const getTags = () => { if (settings) { @@ -239,6 +241,7 @@ const TagLabels: React.FC = ({ canWrite }) => { <> {getTable()}
{getPagination(isDisabled, true)}
+ {isTagMappingFeatureEnabled && TEST} )} @@ -285,6 +288,9 @@ const useMapToProps = ({ query }: TagLabelsMapProps): TagLabelsStateProps => { }, [query, settingsUpdateDisableStatus, settingsUpdateEnableStatus]); return { + isTagMappingFeatureEnabled: useSelector((state: RootState) => + featureFlagsSelectors.selectIsTagMappingFeatureEnabled(state) + ), settings, settingsError, settingsStatus, diff --git a/src/store/featureFlags/__snapshots__/featureFlags.test.ts.snap b/src/store/featureFlags/__snapshots__/featureFlags.test.ts.snap index 7d2bc8a18..4c14bbea0 100644 --- a/src/store/featureFlags/__snapshots__/featureFlags.test.ts.snap +++ b/src/store/featureFlags/__snapshots__/featureFlags.test.ts.snap @@ -9,5 +9,6 @@ exports[`default state 1`] = ` "isIbmFeatureEnabled": false, "isRosFeatureEnabled": false, "isSettingsPlatformFeatureEnabled": false, + "isTagMappingFeatureEnabled": false, } `; diff --git a/src/store/featureFlags/featureFlagsActions.ts b/src/store/featureFlags/featureFlagsActions.ts index afe539868..a9c02af5b 100644 --- a/src/store/featureFlags/featureFlagsActions.ts +++ b/src/store/featureFlags/featureFlagsActions.ts @@ -7,6 +7,7 @@ export interface FeatureFlagsActionMeta { isIbmFeatureEnabled?: boolean; isRosFeatureEnabled?: boolean; isSettingsPlatformFeatureEnabled?: boolean; + isTagMappingFeatureEnabled?: boolean; } export const setFeatureFlags = createAction('feature/init_feature_flags')(); diff --git a/src/store/featureFlags/featureFlagsReducer.ts b/src/store/featureFlags/featureFlagsReducer.ts index 62ea24532..9c077be88 100644 --- a/src/store/featureFlags/featureFlagsReducer.ts +++ b/src/store/featureFlags/featureFlagsReducer.ts @@ -14,6 +14,7 @@ export type FeatureFlagsState = Readonly<{ isIbmFeatureEnabled: boolean; isRosFeatureEnabled: boolean; isSettingsPlatformFeatureEnabled: boolean; + isTagMappingFeatureEnabled: boolean; }>; export const defaultState: FeatureFlagsState = { @@ -24,6 +25,7 @@ export const defaultState: FeatureFlagsState = { isIbmFeatureEnabled: false, isRosFeatureEnabled: false, isSettingsPlatformFeatureEnabled: false, + isTagMappingFeatureEnabled: false, }; export const stateKey = 'featureFlags'; @@ -40,6 +42,7 @@ export function featureFlagsReducer(state = defaultState, action: FeatureFlagsAc isIbmFeatureEnabled: action.payload.isIbmFeatureEnabled, isRosFeatureEnabled: action.payload.isRosFeatureEnabled, isSettingsPlatformFeatureEnabled: action.payload.isSettingsPlatformFeatureEnabled, + isTagMappingFeatureEnabled: action.payload.isTagMappingFeatureEnabled, }; default: diff --git a/src/store/featureFlags/featureFlagsSelectors.ts b/src/store/featureFlags/featureFlagsSelectors.ts index 56b52360b..507f0e4dc 100644 --- a/src/store/featureFlags/featureFlagsSelectors.ts +++ b/src/store/featureFlags/featureFlagsSelectors.ts @@ -16,3 +16,5 @@ export const selectIsIbmFeatureEnabled = (state: RootState) => selectFeatureFlag export const selectIsRosFeatureEnabled = (state: RootState) => selectFeatureFlagsState(state).isRosFeatureEnabled; export const selectIsSettingsPlatformFeatureEnabled = (state: RootState) => selectFeatureFlagsState(state).isSettingsPlatformFeatureEnabled; +export const selectIsTagMappingFeatureEnabled = (state: RootState) => + selectFeatureFlagsState(state).isTagMappingFeatureEnabled; From ec54ecb629e790f5731923341e7de97796df4529 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:50:26 +0000 Subject: [PATCH 10/61] (chore): Bump the lint-dependencies group with 2 updates Bumps the lint-dependencies group with 2 updates: [eslint](https://github.com/eslint/eslint) and [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc). Updates `eslint` from 8.56.0 to 8.57.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.56.0...v8.57.0) Updates `eslint-plugin-jsdoc` from 48.1.0 to 48.2.0 - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v48.1.0...v48.2.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint-dependencies - dependency-name: eslint-plugin-jsdoc dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 26 +++++++++++++------------- package.json | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 915db4d13..9a2405e1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,10 +63,10 @@ "@typescript-eslint/parser": "^7.0.2", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-plugin-formatjs": "^4.12.2", "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-jsdoc": "^48.1.0", + "eslint-plugin-jsdoc": "^48.2.0", "eslint-plugin-markdown": "^3.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", @@ -864,9 +864,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8815,16 +8815,16 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -9389,9 +9389,9 @@ "dev": true }, "node_modules/eslint-plugin-jsdoc": { - "version": "48.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.1.0.tgz", - "integrity": "sha512-g9S8ukmTd1DVcV/xeBYPPXOZ6rc8WJ4yi0+MVxJ1jBOrz5kmxV9gJJQ64ltCqIWFnBChLIhLVx3tbTSarqVyFA==", + "version": "48.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.0.tgz", + "integrity": "sha512-O2B1XLBJnUCRkggFzUQ+PBYJDit8iAgXdlu8ucolqGrbmOWPvttZQZX8d1sC0MbqDMSLs8SHSQxaNPRY1RQREg==", "dev": true, "dependencies": { "@es-joy/jsdoccomment": "~0.42.0", diff --git a/package.json b/package.json index a03338ed5..ec7f24665 100644 --- a/package.json +++ b/package.json @@ -103,10 +103,10 @@ "@typescript-eslint/parser": "^7.0.2", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-plugin-formatjs": "^4.12.2", "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-jsdoc": "^48.1.0", + "eslint-plugin-jsdoc": "^48.2.0", "eslint-plugin-markdown": "^3.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", From bb82c0f04066ee837c6601049f8c7454da15c845 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:54:38 +0000 Subject: [PATCH 11/61] (chore): Bump the ci-dependencies group with 3 updates Bumps the ci-dependencies group with 3 updates: [@redhat-cloud-services/frontend-components](https://github.com/RedHatInsights/frontend-components), [@redhat-cloud-services/frontend-components-utilities](https://github.com/RedHatInsights/frontend-components) and [yaml](https://github.com/eemeli/yaml). Updates `@redhat-cloud-services/frontend-components` from 4.2.3 to 4.2.4 - [Commits](https://github.com/RedHatInsights/frontend-components/commits) Updates `@redhat-cloud-services/frontend-components-utilities` from 4.0.5 to 4.0.6 - [Commits](https://github.com/RedHatInsights/frontend-components/commits) Updates `yaml` from 2.3.4 to 2.4.0 - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.3.4...v2.4.0) --- updated-dependencies: - dependency-name: "@redhat-cloud-services/frontend-components" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ci-dependencies - dependency-name: "@redhat-cloud-services/frontend-components-utilities" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ci-dependencies - dependency-name: yaml dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ci-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 29 ++++++++++++++++------------- package.json | 6 +++--- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 915db4d13..3b068db31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,10 +17,10 @@ "@patternfly/react-icons": "^5.2.0", "@patternfly/react-table": "^5.2.0", "@patternfly/react-tokens": "^5.2.0", - "@redhat-cloud-services/frontend-components": "^4.2.3", + "@redhat-cloud-services/frontend-components": "^4.2.4", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", - "@redhat-cloud-services/frontend-components-utilities": "^4.0.5", + "@redhat-cloud-services/frontend-components-utilities": "^4.0.6", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.1.2", @@ -39,7 +39,7 @@ "typesafe-actions": "^5.1.0", "unleash-proxy-client": "^3.3.1", "victory-core": "36.8.6", - "yaml": "^2.3.4" + "yaml": "^2.4.0" }, "devDependencies": { "@formatjs/cli": "^6.2.7", @@ -2450,9 +2450,9 @@ } }, "node_modules/@redhat-cloud-services/frontend-components": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-4.2.3.tgz", - "integrity": "sha512-mc1HNFupS4EQY1ipZa8Tw/lR8L1J4NEtcSVbvMeKwvfFLlrUIG8isTnEHE+e8bjIcMhnCOO37b0K089uSzJHOw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-4.2.4.tgz", + "integrity": "sha512-5xne/DD1/+txtN50YOrKPeJKpSgENkGz4o4oqYMRB+ElJwnupsaOasvyfMwImKggDcAZ/uWLXt8KNoE8ZXhVvA==", "dependencies": { "@patternfly/react-component-groups": "^5.0.0", "@redhat-cloud-services/frontend-components-utilities": "^4.0.0", @@ -2894,10 +2894,11 @@ "peer": true }, "node_modules/@redhat-cloud-services/frontend-components-utilities": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-4.0.5.tgz", - "integrity": "sha512-cwutgum9dWSROBrrV+ddMwi7VH+CxNV2LciqfdH/uyWXmUVGXPDifghMgkx4Th//VdS3+AVGSkinhIzNej9ArA==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-4.0.6.tgz", + "integrity": "sha512-3GQvBQx/KmTeY0AX2comPjHpCruc6bjOMg5fTPkGxdHtwhfMSrVIi90T5et+7Jn9sFOHV5ZuPGDmym1oYmYTlQ==", "dependencies": { + "@redhat-cloud-services/rbac-client": "^1.0.100", "@redhat-cloud-services/types": "^0.0.24", "@sentry/browser": "^5.30.0", "awesome-debounce-promise": "^2.1.0", @@ -2909,7 +2910,6 @@ "peerDependencies": { "@patternfly/react-core": "^5.0.0", "@patternfly/react-table": "^5.0.0", - "@redhat-cloud-services/rbac-client": "^1.0.100", "cypress": ">=12.0.0 < 14.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -22100,9 +22100,12 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", + "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } diff --git a/package.json b/package.json index a03338ed5..c8cade70d 100644 --- a/package.json +++ b/package.json @@ -57,10 +57,10 @@ "@patternfly/react-icons": "^5.2.0", "@patternfly/react-table": "^5.2.0", "@patternfly/react-tokens": "^5.2.0", - "@redhat-cloud-services/frontend-components": "^4.2.3", + "@redhat-cloud-services/frontend-components": "^4.2.4", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", - "@redhat-cloud-services/frontend-components-utilities": "^4.0.5", + "@redhat-cloud-services/frontend-components-utilities": "^4.0.6", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.1.2", @@ -79,7 +79,7 @@ "typesafe-actions": "^5.1.0", "unleash-proxy-client": "^3.3.1", "victory-core": "36.8.6", - "yaml": "^2.3.4" + "yaml": "^2.4.0" }, "devDependencies": { "@formatjs/cli": "^6.2.7", From 05c18d9d0714dd6cb8aa08f82fe6a832493f4f77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:19:38 +0000 Subject: [PATCH 12/61] Bump sanitize-html from 2.11.0 to 2.12.1 Bumps [sanitize-html](https://github.com/apostrophecms/sanitize-html) from 2.11.0 to 2.12.1. - [Changelog](https://github.com/apostrophecms/sanitize-html/blob/main/CHANGELOG.md) - [Commits](https://github.com/apostrophecms/sanitize-html/compare/2.11.0...2.12.1) --- updated-dependencies: - dependency-name: sanitize-html dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24ccee272..f5d8b0482 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18504,9 +18504,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sanitize-html": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.11.0.tgz", - "integrity": "sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.12.1.tgz", + "integrity": "sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==", "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", From e3bfe128d13ee57900defdbe130090bdfb3344f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 10:06:25 +0000 Subject: [PATCH 13/61] (chore): Bump the lint-dependencies group with 2 updates Bumps the lint-dependencies group with 2 updates: [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react). Updates `@typescript-eslint/parser` from 7.0.2 to 7.1.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.1.0/packages/parser) Updates `eslint-plugin-react` from 7.33.2 to 7.34.0 - [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases) - [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md) - [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.33.2...v7.34.0) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint-dependencies - dependency-name: eslint-plugin-react dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 418 ++++++++++++++++++++++++++++------------------ package.json | 4 +- 2 files changed, 253 insertions(+), 169 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24ccee272..fc75051a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,7 +60,7 @@ "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.0.2", + "@typescript-eslint/parser": "^7.1.0", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", "eslint": "^8.57.0", @@ -70,7 +70,7 @@ "eslint-plugin-markdown": "^3.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react": "^7.34.0", "eslint-plugin-simple-import-sort": "^12.0.0", "eslint-plugin-sort-keys-fix": "^1.1.2", "eslint-plugin-testing-library": "^6.2.0", @@ -4486,15 +4486,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.2.tgz", - "integrity": "sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz", + "integrity": "sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.0.2", - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/typescript-estree": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2", + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/typescript-estree": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", "debug": "^4.3.4" }, "engines": { @@ -4514,13 +4514,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", - "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.0.tgz", + "integrity": "sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2" + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4531,9 +4531,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", - "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz", + "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4544,13 +4544,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", - "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz", + "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4572,12 +4572,12 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", - "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz", + "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/types": "7.1.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -5561,6 +5561,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.findlast": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.4.tgz", + "integrity": "sha512-BMtLxpV+8BD+6ZPFIWmnUBpQoy+A+ujcg4rhp2iwCRJYA7PEh2MS4NL3lz8EiDlLrJPp2hg9qWihr5pd//jcGw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.findlastindex": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", @@ -5616,6 +5635,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.toreversed": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", + "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, "node_modules/array.prototype.tosorted": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", @@ -5741,10 +5772,13 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", - "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -6396,13 +6430,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8135,16 +8174,19 @@ } }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-lazy-prop": { @@ -8606,50 +8648,52 @@ } }, "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "version": "1.22.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", + "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.1", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.0", + "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.8", "string.prototype.trimend": "^1.0.7", "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.5", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -8664,6 +8708,17 @@ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", "dev": true }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", @@ -8693,25 +8748,29 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz", + "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==", "dev": true, "dependencies": { "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", + "es-abstract": "^1.22.4", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.2", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", + "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", + "internal-slot": "^1.0.7", "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" + "safe-array-concat": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-module-lexer": { @@ -8721,14 +8780,14 @@ "dev": true }, "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -9780,27 +9839,29 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.33.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", - "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "version": "7.34.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.0.tgz", + "integrity": "sha512-MeVXdReleBTdkz/bvcQMSnCXGi+c9kvy51IpinjnJgutl3YTHWsDdke7Z1ufZpGfDG8xduBDKyjtB9JH1eBKIQ==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", + "array-includes": "^3.1.7", + "array.prototype.findlast": "^1.2.4", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.toreversed": "^1.1.2", + "array.prototype.tosorted": "^1.1.3", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.12", + "es-iterator-helpers": "^1.0.17", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7", + "object.hasown": "^1.1.3", + "object.values": "^1.1.7", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", + "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.8" + "string.prototype.matchall": "^4.0.10" }, "engines": { "node": ">=4" @@ -11267,11 +11328,11 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.3.tgz", - "integrity": "sha512-JIcZczvcMVE7AUOP+X72bh8HqHBRxFdz5PDHYtNG/lE3yk9b3KZBJlwFcTyPYjg3L4RLLmZJzvjxhaZVapxFrQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { - "es-errors": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", @@ -11309,13 +11370,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" @@ -11583,20 +11645,20 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, @@ -11631,9 +11693,9 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dependencies": { "function-bind": "^1.1.2" }, @@ -12319,12 +12381,12 @@ } }, "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", + "es-errors": "^1.3.0", "hasown": "^2.0.0", "side-channel": "^1.0.4" }, @@ -12708,9 +12770,9 @@ } }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { "node": ">= 0.4" @@ -12836,12 +12898,15 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -17474,6 +17539,15 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.4.33", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", @@ -18165,14 +18239,15 @@ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -18482,13 +18557,13 @@ ] }, "node_modules/safe-regex-test": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", - "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" }, "engines": { @@ -18856,13 +18931,14 @@ } }, "node_modules/set-function-length": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", - "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", "dependencies": { - "define-data-property": "^1.1.1", + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.2", + "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.1" }, @@ -20360,29 +20436,30 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -20392,16 +20469,17 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -20411,14 +20489,20 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", + "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" diff --git a/package.json b/package.json index 0634e4020..3c37c35b1 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.0.2", + "@typescript-eslint/parser": "^7.1.0", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", "eslint": "^8.57.0", @@ -110,7 +110,7 @@ "eslint-plugin-markdown": "^3.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react": "^7.34.0", "eslint-plugin-simple-import-sort": "^12.0.0", "eslint-plugin-sort-keys-fix": "^1.1.2", "eslint-plugin-testing-library": "^6.2.0", From 92e82f63e07dff033b45a346371f84460e209275 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 4 Mar 2024 15:23:16 -0500 Subject: [PATCH 14/61] Fix URL in comment --- fec.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fec.config.js b/fec.config.js index 794c26ab3..e36a70737 100644 --- a/fec.config.js +++ b/fec.config.js @@ -1,4 +1,4 @@ -// Based on https://github.com/RedHatInsights/frontend-components/blob/master/packages/config/src/scripts/dev.webpack.config.js +// Based on https://github.com/RedHatInsights/frontend-components/blob/master/packages/config/src/bin/dev.webpack.config.ts const CopyWebpackPlugin = require('copy-webpack-plugin'); const path = require('path'); From fced45872018047e15df92daf6250ea5c3fc9038 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 4 Mar 2024 19:36:33 -0500 Subject: [PATCH 15/61] Dependency updates --- .github/dependabot.yml | 3 + package-lock.json | 780 ++++++++++++++--------------------------- package.json | 22 +- 3 files changed, 274 insertions(+), 531 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8f7b312a9..c72494c5c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,6 +18,9 @@ updates: lint-dependencies: patterns: - "*eslint*" + patternfly-dependencies: + patterns: + - "@patternfly*" test-dependencies: patterns: - "*jest*" diff --git a/package-lock.json b/package-lock.json index fc75051a4..bc838dedb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "GNU AGPLv3", "dependencies": { "@patternfly/patternfly": "^5.2.0", - "@patternfly/react-charts": "^7.2.0", + "@patternfly/react-charts": "^7.2.1", "@patternfly/react-component-groups": "^5.0.0", "@patternfly/react-core": "^5.2.0", "@patternfly/react-icons": "^5.2.0", @@ -20,10 +20,10 @@ "@redhat-cloud-services/frontend-components": "^4.2.4", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", - "@redhat-cloud-services/frontend-components-utilities": "^4.0.6", + "@redhat-cloud-services/frontend-components-utilities": "^4.0.7", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.2.1", - "@unleash/proxy-client-react": "^4.1.2", + "@unleash/proxy-client-react": "^4.2.2", "axios": "^1.6.7", "date-fns": "^3.3.1", "js-file-download": "^0.4.12", @@ -33,12 +33,12 @@ "react-dom": "^18.2.0", "react-intl": "^6.6.2", "react-redux": "^9.1.0", - "react-router-dom": "^6.22.1", + "react-router-dom": "^6.22.2", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", "unleash-proxy-client": "^3.3.1", - "victory-core": "36.8.6", + "victory-core": "^36.9.1", "yaml": "^2.4.0" }, "devDependencies": { @@ -46,7 +46,7 @@ "@formatjs/ecma402-abstract": "^1.18.2", "@formatjs/icu-messageformat-parser": "^2.7.6", "@redhat-cloud-services/eslint-config-redhat-cloud-services": "^2.0.3", - "@redhat-cloud-services/frontend-components-config": "^6.0.9", + "@redhat-cloud-services/frontend-components-config": "^6.0.10", "@redhat-cloud-services/tsc-transform-imports": "^1.0.7", "@swc/core": "1.3.105", "@swc/jest": "^0.2.36", @@ -54,13 +54,13 @@ "@testing-library/react": "^14.2.1", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", - "@types/qs": "^6.9.11", - "@types/react": "^18.2.57", + "@types/qs": "^6.9.12", + "@types/react": "^18.2.62", "@types/react-dom": "^18.2.19", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.1.0", + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", "eslint": "^8.57.0", @@ -2216,32 +2216,32 @@ "integrity": "sha512-phdsXcCRO+JICFXIKtORxSbOWoBr9zRCgtFTKTJ8hAIzm6wEUCdcHZrvsd+SXNR3q/4b/+KlmHUC4Q4KGUiuYw==" }, "node_modules/@patternfly/react-charts": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-charts/-/react-charts-7.2.0.tgz", - "integrity": "sha512-IDg5KfF4VK/4C8qXfnFz9Tsq041RFJlHOGAH5Blb2ZTzTXp7ssZtuPxZvqqKxnIfAoMO+sy9DM8+vgvYPeH0aw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-charts/-/react-charts-7.2.1.tgz", + "integrity": "sha512-ln24zv08JF7tFWY/SXlV5vKInqBU/2o4DuPg5bVhy/V6X3BjPY1aUn/Yrwqw/9H0NTMZPdIpqVt8066zWHAu8g==", "dependencies": { - "@patternfly/react-styles": "^5.2.0", - "@patternfly/react-tokens": "^5.2.0", + "@patternfly/react-styles": "^5.2.1", + "@patternfly/react-tokens": "^5.2.1", "hoist-non-react-statics": "^3.3.0", "lodash": "^4.17.21", "tslib": "^2.5.0", - "victory-area": "^36.8.1", - "victory-axis": "^36.8.1", - "victory-bar": "^36.8.1", - "victory-box-plot": "^36.8.1", - "victory-chart": "^36.8.1", - "victory-core": "^36.8.1", - "victory-create-container": "^36.8.1", - "victory-cursor-container": "^36.8.1", - "victory-group": "^36.8.1", - "victory-legend": "^36.8.1", - "victory-line": "^36.8.1", - "victory-pie": "^36.8.1", - "victory-scatter": "^36.8.1", - "victory-stack": "^36.8.1", - "victory-tooltip": "^36.8.1", - "victory-voronoi-container": "^36.8.1", - "victory-zoom-container": "^36.8.1" + "victory-area": "^36.9.1", + "victory-axis": "^36.9.1", + "victory-bar": "^36.9.1", + "victory-box-plot": "^36.9.1", + "victory-chart": "^36.9.1", + "victory-core": "^36.9.1", + "victory-create-container": "^36.9.1", + "victory-cursor-container": "^36.9.1", + "victory-group": "^36.9.1", + "victory-legend": "^36.9.1", + "victory-line": "^36.9.1", + "victory-pie": "^36.9.1", + "victory-scatter": "^36.9.1", + "victory-stack": "^36.9.1", + "victory-tooltip": "^36.9.1", + "victory-voronoi-container": "^36.9.1", + "victory-zoom-container": "^36.9.1" }, "peerDependencies": { "react": "^17 || ^18", @@ -2291,9 +2291,9 @@ } }, "node_modules/@patternfly/react-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-5.2.0.tgz", - "integrity": "sha512-u8in9RSU8YzcT0npgVeiIHi1Bdp7UdER9azWGi7vlJWooRI1hgQjIDpm22wopGFg0h8VOqhfIFWIyvqxuzhW6A==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-5.2.1.tgz", + "integrity": "sha512-GT96hzI1QenBhq6Pfc51kxnj9aVLjL1zSLukKZXcYVe0HPOy0BFm90bT1Fo4e/z7V9cDYw4SqSX1XLc3O4jsTw==" }, "node_modules/@patternfly/react-table": { "version": "5.2.0", @@ -2313,9 +2313,9 @@ } }, "node_modules/@patternfly/react-tokens": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-5.2.0.tgz", - "integrity": "sha512-ZsrLpStHJQfvUJLIXT+cObJbA3jM4r9iWwULLva0s7DzznXJ6iIACQQfgwDtcSVyM95z5S1a/LHPj/wYgaqUIg==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-5.2.1.tgz", + "integrity": "sha512-8GYz/jnJTGAWUJt5eRAW5dtyiHPKETeFJBPGHaUQnvi/t1ZAkoy8i4Kd/RlHsDC7ktiu813SKCmlzwBwldAHKg==" }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", @@ -2476,9 +2476,9 @@ } }, "node_modules/@redhat-cloud-services/frontend-components-config": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-config/-/frontend-components-config-6.0.9.tgz", - "integrity": "sha512-2W0AHnCvGPKF1WTnMFdEkF/A/TCtHo+biDhH5MdJZzITvBbi4fXTn0IbdN9BVqnwrRujJO273TQ5aVU1WiIb0w==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-config/-/frontend-components-config-6.0.10.tgz", + "integrity": "sha512-h1c72gKQVLmd52ZNQ2x95UJM2ACP1bBgct3cbNW1AyygYEMoWE1x7XYEpclT/7NMdu52L7f4mw0c0sdVusPkdA==", "dev": true, "dependencies": { "@pmmmwh/react-refresh-webpack-plugin": "^0.5.8", @@ -2486,7 +2486,7 @@ "@redhat-cloud-services/tsc-transform-imports": "^1.0.3", "@swc/core": "^1.3.76", "assert": "^2.0.0", - "axios": "^0.27.2", + "axios": "^0.28.0", "browserify-zlib": "^0.2.0", "buffer": "^6.0.3", "chalk": "^4.1.2", @@ -2626,13 +2626,14 @@ } }, "node_modules/@redhat-cloud-services/frontend-components-config/node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.28.0.tgz", + "integrity": "sha512-Tu7NYoGY4Yoc7I+Npf9HhUMtEEpV7ZiLH9yndTCoNhcpBH0kwcvFbzYN9/u5QKI5A6uefjsNNWaz5olJVYS62Q==", "dev": true, "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/@redhat-cloud-services/frontend-components-config/node_modules/chalk": { @@ -2894,9 +2895,9 @@ "peer": true }, "node_modules/@redhat-cloud-services/frontend-components-utilities": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-4.0.6.tgz", - "integrity": "sha512-3GQvBQx/KmTeY0AX2comPjHpCruc6bjOMg5fTPkGxdHtwhfMSrVIi90T5et+7Jn9sFOHV5ZuPGDmym1oYmYTlQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-utilities/-/frontend-components-utilities-4.0.7.tgz", + "integrity": "sha512-Mr0u1rThsspet0SkR12mQRYO2xbA4zKCxSpHs/QKLlSIgQRKqVzmdf7J2SSXGXJJ9FeSE2wXJdI8a2F9TVVQnQ==", "dependencies": { "@redhat-cloud-services/rbac-client": "^1.0.100", "@redhat-cloud-services/types": "^0.0.24", @@ -3007,9 +3008,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.1.tgz", - "integrity": "sha512-zcU0gM3z+3iqj8UX45AmWY810l3oUmXM7uH4dt5xtzvMhRtYVhKGOmgOd1877dOPPepfCjUv57w+syamWIYe7w==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz", + "integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==", "engines": { "node": ">=14.0.0" } @@ -3756,9 +3757,9 @@ } }, "node_modules/@types/d3-path": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.2.tgz", - "integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" }, "node_modules/@types/d3-scale": { "version": "4.0.8", @@ -4053,9 +4054,9 @@ "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/qs": { - "version": "6.9.11", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", - "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.12.tgz", + "integrity": "sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==", "dev": true }, "node_modules/@types/range-parser": { @@ -4065,9 +4066,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.57", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.57.tgz", - "integrity": "sha512-ZvQsktJgSYrQiMirAN60y4O/LRevIV8hUzSOSNB6gfR3/o3wCBFQx3sPwIYtuDMeiVgsSS3UzCV26tEzgnfvQw==", + "version": "18.2.62", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.62.tgz", + "integrity": "sha512-l3f57BbaEKP0xcFzf+5qRG8/PXykZiuVM6eEoPtqBPCp6dxO3HhDkLIgIyXPhPKNAeXn3KO2pEaNgzaEo/asaw==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4306,16 +4307,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.2.tgz", - "integrity": "sha512-/XtVZJtbaphtdrWjr+CJclaCVGPtOdBpFEnvtNf/jRV0IiEemRrL0qABex/nEt8isYcnFacm3nPHYQwL+Wb7qg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.1.tgz", + "integrity": "sha512-zioDz623d0RHNhvx0eesUmGfIjzrk18nSBC8xewepKXbBvN/7c1qImV7Hg8TI1URTxKax7/zxfxj3Uph8Chcuw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.0.2", - "@typescript-eslint/type-utils": "7.0.2", - "@typescript-eslint/utils": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2", + "@typescript-eslint/scope-manager": "7.1.1", + "@typescript-eslint/type-utils": "7.1.1", + "@typescript-eslint/utils": "7.1.1", + "@typescript-eslint/visitor-keys": "7.1.1", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -4341,13 +4342,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", - "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz", + "integrity": "sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2" + "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/visitor-keys": "7.1.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4358,9 +4359,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", - "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.1.tgz", + "integrity": "sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4371,13 +4372,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", - "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz", + "integrity": "sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2", + "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/visitor-keys": "7.1.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4399,17 +4400,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz", - "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.1.tgz", + "integrity": "sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.0.2", - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/scope-manager": "7.1.1", + "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/typescript-estree": "7.1.1", "semver": "^7.5.4" }, "engines": { @@ -4424,12 +4425,12 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", - "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz", + "integrity": "sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/types": "7.1.1", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -4486,15 +4487,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz", - "integrity": "sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.1.tgz", + "integrity": "sha512-ZWUFyL0z04R1nAEgr9e79YtV5LbafdOtN7yapNbn1ansMyaegl2D4bL7vHoJ4HPSc4CaLwuCVas8CVuneKzplQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.1.0", - "@typescript-eslint/types": "7.1.0", - "@typescript-eslint/typescript-estree": "7.1.0", - "@typescript-eslint/visitor-keys": "7.1.0", + "@typescript-eslint/scope-manager": "7.1.1", + "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/typescript-estree": "7.1.1", + "@typescript-eslint/visitor-keys": "7.1.1", "debug": "^4.3.4" }, "engines": { @@ -4514,13 +4515,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.0.tgz", - "integrity": "sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz", + "integrity": "sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.0", - "@typescript-eslint/visitor-keys": "7.1.0" + "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/visitor-keys": "7.1.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4531,9 +4532,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz", - "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.1.tgz", + "integrity": "sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4544,13 +4545,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz", - "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz", + "integrity": "sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.0", - "@typescript-eslint/visitor-keys": "7.1.0", + "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/visitor-keys": "7.1.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4572,12 +4573,12 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz", - "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz", + "integrity": "sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/types": "7.1.1", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -4651,13 +4652,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.2.tgz", - "integrity": "sha512-IKKDcFsKAYlk8Rs4wiFfEwJTQlHcdn8CLwLaxwd6zb8HNiMcQIFX9sWax2k4Cjj7l7mGS5N1zl7RCHOVwHq2VQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.1.tgz", + "integrity": "sha512-5r4RKze6XHEEhlZnJtR3GYeCh1IueUHdbrukV2KSlLXaTjuSfeVF8mZUVPLovidCuZfbVjfhi4c0DNSa/Rdg5g==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.0.2", - "@typescript-eslint/utils": "7.0.2", + "@typescript-eslint/typescript-estree": "7.1.1", + "@typescript-eslint/utils": "7.1.1", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -4678,13 +4679,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz", - "integrity": "sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz", + "integrity": "sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2" + "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/visitor-keys": "7.1.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4695,9 +4696,9 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.2.tgz", - "integrity": "sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.1.tgz", + "integrity": "sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4708,13 +4709,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz", - "integrity": "sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz", + "integrity": "sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/visitor-keys": "7.0.2", + "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/visitor-keys": "7.1.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4736,17 +4737,17 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.2.tgz", - "integrity": "sha512-PZPIONBIB/X684bhT1XlrkjNZJIEevwkKDsdwfiu1WeqBxYEEdIgVDgm8/bbKHVu+6YOpeRqcfImTdImx/4Bsw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.1.tgz", + "integrity": "sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.0.2", - "@typescript-eslint/types": "7.0.2", - "@typescript-eslint/typescript-estree": "7.0.2", + "@typescript-eslint/scope-manager": "7.1.1", + "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/typescript-estree": "7.1.1", "semver": "^7.5.4" }, "engines": { @@ -4761,12 +4762,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz", - "integrity": "sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz", + "integrity": "sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.0.2", + "@typescript-eslint/types": "7.1.1", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -4990,9 +4991,9 @@ "dev": true }, "node_modules/@unleash/proxy-client-react": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@unleash/proxy-client-react/-/proxy-client-react-4.1.2.tgz", - "integrity": "sha512-AW6CKMHfLoUDzLahXHTD9bCKMKC31kC0kYLDrg089vf9gTAuGNnrY0bIEmrkh0M06JVkbXq9H8As3EIXdioizg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@unleash/proxy-client-react/-/proxy-client-react-4.2.2.tgz", + "integrity": "sha512-KRg1dAQfxLSBe8O2i6GIGDM+7HUdUu/ntREy+JeYbmJ2b6ApFtLX/wt+3T/CjrTYjwRlZKZYJDx5E4fRDOQqpQ==", "engines": { "node": ">=16.0.0" }, @@ -18076,11 +18077,11 @@ } }, "node_modules/react-router": { - "version": "6.22.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.1.tgz", - "integrity": "sha512-0pdoRGwLtemnJqn1K0XHUbnKiX0S4X8CgvVVmHGOWmofESj31msHo/1YiqcJWK7Wxfq2a4uvvtS01KAQyWK/CQ==", + "version": "6.22.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz", + "integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==", "dependencies": { - "@remix-run/router": "1.15.1" + "@remix-run/router": "1.15.2" }, "engines": { "node": ">=14.0.0" @@ -18090,12 +18091,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.22.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.1.tgz", - "integrity": "sha512-iwMyyyrbL7zkKY7MRjOVRy+TMnS/OPusaFVxM2P11x9dzSzGmLsebkCvYirGq0DWB9K9hOspHYYtDz33gE5Duw==", + "version": "6.22.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz", + "integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==", "dependencies": { - "@remix-run/router": "1.15.1", - "react-router": "6.22.1" + "@remix-run/router": "1.15.2", + "react-router": "6.22.2" }, "engines": { "node": ">=14.0.0" @@ -20821,494 +20822,259 @@ } }, "node_modules/victory-area": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-area/-/victory-area-36.9.0.tgz", - "integrity": "sha512-4oiQJe+1hVqYyTS08iUwy6NxOqgKS2vj0laM169r/AMqART002z9YHTd2PB958xD3qXTpUbdi0CmvglpEyOqpQ==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-area/-/victory-area-36.9.1.tgz", + "integrity": "sha512-rElzHXJBXZ6sFkYs10aFUoUikFI48XZLbFIfL1tzdA74T426fTRQZNlKvjb2s3XL4fcecqVpvlg1I2dkaAszIQ==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0", - "victory-vendor": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-area/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1", + "victory-vendor": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-axis": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-axis/-/victory-axis-36.9.0.tgz", - "integrity": "sha512-tFNr5S9QInDFxgYRJuLmkInFjqWmnAHhYEP+sGp8yv0y9Amiq/Vkeign2ITSxEVn/TOqSOiNV82UCxR+3rn2xg==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-axis/-/victory-axis-36.9.1.tgz", + "integrity": "sha512-s23wAFlE2KFSb6pRlmY4GXL7ZC2poL7jfUJbVWovBDkIUiz5G020ba2+RfMBL4tBTK006OPzQ3GeUPASG7qejA==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-axis/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-bar": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-36.9.0.tgz", - "integrity": "sha512-t2XbSi1CXgwVkCipkQLxggKxMHv5dI5FG6EwBxuK6gSeouwZCdkWczgjX3X/vpNnNql/N/KOCAOmhv4KbgA8KQ==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-36.9.1.tgz", + "integrity": "sha512-XCPKgeSBFItux1dBFpTZD90uqMw0wgd4+xD+sRgagVthTdppS3JV4YPNo1MxC/Gdm6XQfBFckcFpNG1qm3Noqw==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0", - "victory-vendor": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-bar/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1", + "victory-vendor": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-box-plot": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-box-plot/-/victory-box-plot-36.9.0.tgz", - "integrity": "sha512-esOYzfo0Tyv43fRRQWCfOgIX3aJmKOcKWuspVuTZJX02uT/PXN6aAQRKIjHZYS/0t9grpWLb5ecksuvy6Qalng==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-box-plot/-/victory-box-plot-36.9.1.tgz", + "integrity": "sha512-+dSHrA1naP5xEuVeIEoRadE8VL0+QmobJ6qwTxhZyjSwR9CGOelFZEgK4oVzWb7pfSa3dYUlXQRc+UWG02zFVw==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0", - "victory-vendor": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-box-plot/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1", + "victory-vendor": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-brush-container": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-36.9.0.tgz", - "integrity": "sha512-t25+S6b6IVzKfNIokEfKndIN0wtrlkBazDJcPOn7ogSbEQx6mJfPn8yP6R2C5qXhZcSv9LaD6vcc5+IYw21FMg==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-36.9.1.tgz", + "integrity": "sha512-XyLqCQ1LV1QbnWJh1ZlNxzk5Yp8PHqzGH6HLcnnKodZE8FBWTSREgELMQCrcT9NczI2GAA7XNkhGkZcJ4SuBMw==", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-brush-container/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-chart": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-36.9.0.tgz", - "integrity": "sha512-gnSAJYLXd5HDda6yxlfBYk0GZXqXLRnDDBjKpyEFrYDYoZDaaWom2zXx+eNsdxZW1JxGsBWDFT/JImeAwOogcw==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-36.9.1.tgz", + "integrity": "sha512-i87Ok1vAeY9LirQt6T7B8tSr7d1vAuZvVv7f1MTTlRLHEAvifBNiGrhZho5ETzvTOXOAM7UjwqzPZze0Gk66cA==", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-axis": "^36.9.0", - "victory-core": "^36.9.0", - "victory-polar-axis": "^36.9.0", - "victory-shared-events": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-chart/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-axis": "^36.9.1", + "victory-core": "^36.9.1", + "victory-polar-axis": "^36.9.1", + "victory-shared-events": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-core": { - "version": "36.8.6", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.8.6.tgz", - "integrity": "sha512-fT8SYy4wmtNTloV3npRC9pwEG8UkFF48ZmNGKywykpRuJXHVgV1GXeFEDH8/Ra0qHACVciR9Tcjk6X89GTRw4A==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.1.tgz", + "integrity": "sha512-voPTyOyyVipzJPjelxvszVixiI98ApMNb6X9qfaFYK7fHyavF/Hy4sf/Hwq1otatLI7zpr2hC4wF+af6HDELqA==", "dependencies": { "lodash": "^4.17.21", - "prop-types": "^15.8.1", "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.8.6" + "victory-vendor": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-create-container": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-36.9.0.tgz", - "integrity": "sha512-2lO81PW35HlcjsuwZOTwRT2nHHHGNzIZBsMWg88KsgAajN6ojQYzeH11azABTk5mAfmO0txFuuSFvpqneTuHHA==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-36.9.1.tgz", + "integrity": "sha512-L1c66whZAFnChVQdU2E0aYiTy3Wc1cM58V2vZPo1ORea/W9h3ojOW2bpYkG/XLf67PgnFZ299i23UzuC16Z5uw==", "dependencies": { "lodash": "^4.17.19", - "victory-brush-container": "^36.9.0", - "victory-core": "^36.9.0", - "victory-cursor-container": "^36.9.0", - "victory-selection-container": "^36.9.0", - "victory-voronoi-container": "^36.9.0", - "victory-zoom-container": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-create-container/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-brush-container": "^36.9.1", + "victory-core": "^36.9.1", + "victory-cursor-container": "^36.9.1", + "victory-selection-container": "^36.9.1", + "victory-voronoi-container": "^36.9.1", + "victory-zoom-container": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-cursor-container": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-36.9.0.tgz", - "integrity": "sha512-yG33xBgaM/eprQSzAbypTD/fdjJvUCEZhijvcOrDNSKpHJ/hM6xKZXgVnSAxuHk33K8Dpx4xmjIc9R/sYVN23A==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-36.9.1.tgz", + "integrity": "sha512-jAxlHbebVjIvmyUBf2AVbfk3rpQNyWPSVoozcBAzjDKhrUn5GIPvytg8QvFsShwdCtSob1eSyBEsGkb16F6xnw==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-cursor-container/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-group": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-36.9.0.tgz", - "integrity": "sha512-DOvOb8ei6diW1XIjh5tv/eHB2tldaryiHGAA87DKnTJApMCUpjFrVSqUNycAIh3UH9/K/sqFzbQlNdHO5vG61g==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-36.9.1.tgz", + "integrity": "sha512-FJwZbrwMJSR/ucj4rYXKYJ+R6oDNsHPG2OvVs4KWkMSSp1Ld/0/V42qFqFNixcLAEcD5ACYtyigZOmS8VEnSnA==", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.0", - "victory-shared-events": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-group/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1", + "victory-shared-events": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-legend": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-36.9.0.tgz", - "integrity": "sha512-tc6YSskNasm/oW3GTxsbfIyHsKzn4DDWRAT4VPVw0z5RzsZy+7WW+H2ku34Ev6k6Zk0wa0ahSYIiFgFV1feoOw==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-36.9.1.tgz", + "integrity": "sha512-NVWJzEJgm2+LH94b6aUQ96M58TzAgKP9wXlQC/CuYLMqK45RiLwg7pkSNuXBdtQiJgpD3W6d6klHQmUP2JkNzA==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-legend/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-line": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-36.9.0.tgz", - "integrity": "sha512-TJR5EwXs1Cp8Zg8FUPILyNgBuIkffya0pbHQ72/EYUA+BOqovjZe3/Qcl409SyRy6SOkyawxsSqhkjrEsODuBg==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-36.9.1.tgz", + "integrity": "sha512-WfnDMI5mYN+7rC21yG3IXLIkGL+xNPAFDYikCwtKD9MnHUqk1k/HMGTH0BCVPgXagwIzd8aGpbJGlvcfRr1Kvg==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0", - "victory-vendor": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-line/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1", + "victory-vendor": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-pie": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-36.9.0.tgz", - "integrity": "sha512-GPMvod6L8VcEUcEaOgjIRshbUYUN58FGcbMAvnz0yyiuqIR8eIp+VXnnOe1P2mZg35pUrcoHzKqRTsQGQAQYSw==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-36.9.1.tgz", + "integrity": "sha512-TjfGe1Wd8cWaV7Ldd2AgPstAT0qbxY8EHYj2YyB93qfZCwdLQqxEmDobl+T+BmnRtCodXUWdghkLvVggf4N0bQ==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0", - "victory-vendor": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-pie/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1", + "victory-vendor": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-polar-axis": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-polar-axis/-/victory-polar-axis-36.9.0.tgz", - "integrity": "sha512-nhxL4+fGjyLNnSoj6FD3fZSnAco9k5xsRbE0WXSRrr0RQCsBGUjN/ErOL19LRbSwh39fs3m+SGIC0GPkAPC1nQ==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-polar-axis/-/victory-polar-axis-36.9.1.tgz", + "integrity": "sha512-C7oPeRzG0Mn+Veu8qI1lVgiBMyZwdrvnplUi6AnFvYf9wURoFjyC+DQ7Eh5IH4TeVQz9rr9ksiliFtXPOHCwvg==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-polar-axis/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-scatter": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-36.9.0.tgz", - "integrity": "sha512-q03QFbApuaureOT9+1m3GSS9SSupnoJWy3UwQhRNB1i4WtIQF5G89E6R/LJmTDvzJmf0bj+p5DMhyujBk36mig==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-36.9.1.tgz", + "integrity": "sha512-n5h/PUW2pHwiBJi0gLt5D5/jM3ZNXnFqZyjFkiKP6nztUtLRQfjcDMwmRWFOF/WZS/e2C7qMYizuXmxuU5ZVOw==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-scatter/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-selection-container": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-36.9.0.tgz", - "integrity": "sha512-jXl84CSxE29a6MEOv0LzK4bh51VdvZuWLif7VBILRIFSTB11Rg16mcfgDX0xanmkZCcjC2e18tlDW3eN2x7LxQ==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-36.9.1.tgz", + "integrity": "sha512-yugHpsS+JHmhJdhduuDHIBVg0mJ60Nge52CCHdiqM7hitcK1+hJgeEPt9zyCDYivQrBimRCGjNYfXhjjCbxzrg==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-selection-container/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-shared-events": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-36.9.0.tgz", - "integrity": "sha512-Ucwg31n3XldQ5nOS79ZJoBu96j3wE/v9bVrbQBTrMO/GSw/pchGl5NNgWYfywRFNNBDsMJKYRHzsXaM9Ki0fSQ==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-36.9.1.tgz", + "integrity": "sha512-U+iDeuv17qYbigMivQcYmZPrvCMHQ8oHFprrlmF9K9cby3q9NFuZ6bbZUngm8kB61P0L6gR0BbYSWvdT9QUEbA==", "dependencies": { "json-stringify-safe": "^5.0.1", "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-shared-events/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-stack": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-36.9.0.tgz", - "integrity": "sha512-BLhCVFGzcG3Dn/eaJ9vlQCTMbBjeuKhJqZnAMNbcJNQBYJB4VtpR6sbZdWxvouZ9k30SSD5AzgpEwwTv8ooVyw==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-36.9.1.tgz", + "integrity": "sha512-yTSLyq3PShJIIsHFjRZcWJvJsZU0+kZ6OhYawqnE133XkaQFdA6C4nhMGCAs6VzFT9PofzFuU0OY4geZ70G1TQ==", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.0", - "victory-shared-events": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-stack/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1", + "victory-shared-events": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-tooltip": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-36.9.0.tgz", - "integrity": "sha512-774IH8aT4PmP9VBGsyMFdJag7XsbYEzRZUmpUPZ47KTuESwUAMTfjGghPJjTQ1eXdajpehD9/oo+Jo9rnca7sQ==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-36.9.1.tgz", + "integrity": "sha512-/ICZ4jaYFplSgK1HkFikEN9d4xlRm7dI7MouYTC1m74q869nMPycLJeVjUo9RsiPnUDeiJLAnKZnXb0oICyYsQ==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-tooltip/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-vendor": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.0.tgz", - "integrity": "sha512-n1A0J1xgwHb5nh56M0d8XlQabMCeTktvEqqr5WNAHspWEsVVGGaaaRg0TcQUtyC1akX0Cox1lMZdIv0Jl7o0ew==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.1.tgz", + "integrity": "sha512-+pZIP+U3pEJdDCeFmsXwHzV7vNHQC/eIbHklfe2ZCZqayYRH7lQbHcVgsJ0XOOv27hWs4jH4MONgXxHMObTMSA==", "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", @@ -21327,53 +21093,27 @@ } }, "node_modules/victory-voronoi-container": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-36.9.0.tgz", - "integrity": "sha512-cEackV1/+Nmq/A3SC9A8gh09AzwQJyWsmGo3osWs2hnDGlxB0gGuj3bRQ/ZNGh0Vzm14Dlhs1u21v7Rbh3IGfQ==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-36.9.1.tgz", + "integrity": "sha512-F/ZWvhF/JkRxFT1UPGf4HgPnBAhUmbRIBssAvsIRer4cr3p7RieMNTMcTYHtVwR9kTKClfmJKgn1T7imBGt2BA==", "dependencies": { "delaunay-find": "0.0.6", "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.0", - "victory-tooltip": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-voronoi-container/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1", + "victory-tooltip": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-zoom-container": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-36.9.0.tgz", - "integrity": "sha512-kODhPyJooO8WRiz9Fpch/RVV4uXoMyYNHjzbeX6t88n25A86w5I5DhUIM+GUGlU7AGvuwLMCa/Te8P9B9JXDaQ==", + "version": "36.9.1", + "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-36.9.1.tgz", + "integrity": "sha512-2G+2iUsmTCpt1ItUWVOzK0CCRYCFf+/rH4uXuvXqipHjRnotz5bxOkuW68Fdx1MzGoexIc8DfQoKxKd/q0HkZA==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.0" - }, - "peerDependencies": { - "react": ">=16.6.0" - } - }, - "node_modules/victory-zoom-container/node_modules/victory-core": { - "version": "36.9.0", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.0.tgz", - "integrity": "sha512-RawQkhPQaV4Oi+8KLHm8mzEwxL6mirdQOcn5+BaGrUnP9MK7jjEdjzNvDESuJdk/bDuemXvWEEriG+ofDmvzfQ==", - "dependencies": { - "lodash": "^4.17.21", - "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.0" + "victory-core": "^36.9.1" }, "peerDependencies": { "react": ">=16.6.0" diff --git a/package.json b/package.json index 3c37c35b1..057fa4d33 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ }, "dependencies": { "@patternfly/patternfly": "^5.2.0", - "@patternfly/react-charts": "^7.2.0", + "@patternfly/react-charts": "^7.2.1", "@patternfly/react-component-groups": "^5.0.0", "@patternfly/react-core": "^5.2.0", "@patternfly/react-icons": "^5.2.0", @@ -60,10 +60,10 @@ "@redhat-cloud-services/frontend-components": "^4.2.4", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", - "@redhat-cloud-services/frontend-components-utilities": "^4.0.6", + "@redhat-cloud-services/frontend-components-utilities": "^4.0.7", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.2.1", - "@unleash/proxy-client-react": "^4.1.2", + "@unleash/proxy-client-react": "^4.2.2", "axios": "^1.6.7", "date-fns": "^3.3.1", "js-file-download": "^0.4.12", @@ -73,12 +73,12 @@ "react-dom": "^18.2.0", "react-intl": "^6.6.2", "react-redux": "^9.1.0", - "react-router-dom": "^6.22.1", + "react-router-dom": "^6.22.2", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", "unleash-proxy-client": "^3.3.1", - "victory-core": "36.8.6", + "victory-core": "^36.9.1", "yaml": "^2.4.0" }, "devDependencies": { @@ -86,7 +86,7 @@ "@formatjs/ecma402-abstract": "^1.18.2", "@formatjs/icu-messageformat-parser": "^2.7.6", "@redhat-cloud-services/eslint-config-redhat-cloud-services": "^2.0.3", - "@redhat-cloud-services/frontend-components-config": "^6.0.9", + "@redhat-cloud-services/frontend-components-config": "^6.0.10", "@redhat-cloud-services/tsc-transform-imports": "^1.0.7", "@swc/core": "1.3.105", "@swc/jest": "^0.2.36", @@ -94,13 +94,13 @@ "@testing-library/react": "^14.2.1", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", - "@types/qs": "^6.9.11", - "@types/react": "^18.2.57", + "@types/qs": "^6.9.12", + "@types/react": "^18.2.62", "@types/react-dom": "^18.2.19", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "^7.0.2", - "@typescript-eslint/parser": "^7.1.0", + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", "eslint": "^8.57.0", @@ -131,7 +131,7 @@ "webpack-bundle-analyzer": "^4.10.1" }, "overrides": { - "@typescript-eslint/eslint-plugin": "^7.0.2", + "@typescript-eslint/eslint-plugin": "^7.1.1", "redux": "^5.0.1" }, "insights": { From f9490a2271558eb3fcf4c5ebfcdc86b6e7632ff2 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 4 Mar 2024 19:23:27 -0500 Subject: [PATCH 16/61] Unleash's UserIDs strategy should use an account IDs https://issues.redhat.com/browse/COST-4733 --- src/components/featureFlags/featureFlags.tsx | 63 ++++---------------- 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/src/components/featureFlags/featureFlags.tsx b/src/components/featureFlags/featureFlags.tsx index 69af98cff..ca26d5dc1 100644 --- a/src/components/featureFlags/featureFlags.tsx +++ b/src/components/featureFlags/featureFlags.tsx @@ -1,6 +1,6 @@ import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome'; -import { useUnleashClient, useUnleashContext } from '@unleash/proxy-client-react'; -import { useLayoutEffect, useRef } from 'react'; +import { useUnleashClient } from '@unleash/proxy-client-react'; +import { useLayoutEffect } from 'react'; import { useDispatch } from 'react-redux'; import { featureFlagsActions } from 'store/featureFlags'; @@ -18,59 +18,22 @@ export const enum FeatureToggle { // The FeatureFlags component saves feature flags in store for places where Unleash hooks not available const useFeatureFlags = () => { - const updateContext = useUnleashContext(); const client = useUnleashClient(); const dispatch = useDispatch(); - const { auth, isBeta } = useChrome(); - - const fetchUser = callback => { - auth.getUser().then(user => { - callback((user as any).identity.account_number); - }); - }; - - const isMounted = useRef(false); - useLayoutEffect(() => { - isMounted.current = true; - return () => { - isMounted.current = false; - }; - }, []); - - // Update everytime or flags may be false - useLayoutEffect(() => { - fetchUser(userId => { - if (isMounted.current) { - updateContext({ - userId, - }); - } - }); - }); + const { isBeta } = useChrome(); useLayoutEffect(() => { - // Wait for the new flags to pull in from the different context - const fetchFlags = async userId => { - await updateContext({ userId }).then(() => { - dispatch( - featureFlagsActions.setFeatureFlags({ - isClusterInfoFeatureEnabled: client.isEnabled(FeatureToggle.clusterInfo), - isExportsFeatureEnabled: client.isEnabled(FeatureToggle.exports), - isFinsightsFeatureEnabled: client.isEnabled(FeatureToggle.finsights), - isIbmFeatureEnabled: client.isEnabled(FeatureToggle.ibm), - isRosFeatureEnabled: - client.isEnabled(FeatureToggle.ros) || (client.isEnabled(FeatureToggle.rosBeta) && isBeta()), // Need to check beta in prod - isSettingsPlatformFeatureEnabled: client.isEnabled(FeatureToggle.settingsPlatform), - isTagMappingFeatureEnabled: client.isEnabled(FeatureToggle.tagMapping), - }) - ); - }); + // Workaround for code that doesn't use hooks + const flags = { + isClusterInfoFeatureEnabled: client.isEnabled(FeatureToggle.clusterInfo), + isExportsFeatureEnabled: client.isEnabled(FeatureToggle.exports), + isFinsightsFeatureEnabled: client.isEnabled(FeatureToggle.finsights), + isIbmFeatureEnabled: client.isEnabled(FeatureToggle.ibm), + isRosFeatureEnabled: client.isEnabled(FeatureToggle.ros) || (client.isEnabled(FeatureToggle.rosBeta) && isBeta()), // Need to check beta in prod + isSettingsPlatformFeatureEnabled: client.isEnabled(FeatureToggle.settingsPlatform), + isTagMappingFeatureEnabled: client.isEnabled(FeatureToggle.tagMapping), }; - fetchUser(userId => { - if (isMounted.current) { - fetchFlags(userId); - } - }); + dispatch(featureFlagsActions.setFeatureFlags(flags)); }); }; From d1c66ea609d7c8c17e458f9de87cbe9d2f3c38bf Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 4 Mar 2024 20:37:20 -0500 Subject: [PATCH 17/61] Unleash's UserIDs strategy should use an account IDs https://issues.redhat.com/browse/COST-4733 --- src/routes/components/charts/costChart/costChart.tsx | 2 +- .../components/charts/costExplorerChart/costExplorerChart.tsx | 2 +- src/routes/components/charts/dailyCostChart/dailyCostChart.tsx | 2 +- .../components/charts/dailyTrendChart/dailyTrendChart.tsx | 2 +- .../charts/historicalCostChart/historicalCostChart.tsx | 2 +- .../charts/historicalTrendChart/historicalTrendChart.tsx | 2 +- .../charts/historicalUsageChart/historicalUsageChart.tsx | 2 +- src/routes/components/charts/trendChart/trendChart.tsx | 2 +- src/routes/components/charts/usageChart/usageChart.tsx | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/routes/components/charts/costChart/costChart.tsx b/src/routes/components/charts/costChart/costChart.tsx index 672697766..b5f3fa232 100644 --- a/src/routes/components/charts/costChart/costChart.tsx +++ b/src/routes/components/charts/costChart/costChart.tsx @@ -327,7 +327,7 @@ class CostChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} + title={(datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} /> ), } as any) diff --git a/src/routes/components/charts/costExplorerChart/costExplorerChart.tsx b/src/routes/components/charts/costExplorerChart/costExplorerChart.tsx index 8d1a161e4..4760d2fbe 100644 --- a/src/routes/components/charts/costExplorerChart/costExplorerChart.tsx +++ b/src/routes/components/charts/costExplorerChart/costExplorerChart.tsx @@ -443,7 +443,7 @@ class CostExplorerChartBase extends React.Component intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} + title={(datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} /> ), } as any) diff --git a/src/routes/components/charts/dailyCostChart/dailyCostChart.tsx b/src/routes/components/charts/dailyCostChart/dailyCostChart.tsx index 7ae5bcf50..b07e2eece 100644 --- a/src/routes/components/charts/dailyCostChart/dailyCostChart.tsx +++ b/src/routes/components/charts/dailyCostChart/dailyCostChart.tsx @@ -378,7 +378,7 @@ class DailyCostChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} + title={(datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} /> ), } as any) diff --git a/src/routes/components/charts/dailyTrendChart/dailyTrendChart.tsx b/src/routes/components/charts/dailyTrendChart/dailyTrendChart.tsx index 9bd4ef8b2..7dfdb4b49 100644 --- a/src/routes/components/charts/dailyTrendChart/dailyTrendChart.tsx +++ b/src/routes/components/charts/dailyTrendChart/dailyTrendChart.tsx @@ -400,7 +400,7 @@ class DailyTrendChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} + title={(datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} /> ), } as any) diff --git a/src/routes/components/charts/historicalCostChart/historicalCostChart.tsx b/src/routes/components/charts/historicalCostChart/historicalCostChart.tsx index da0c4ed31..77a33bf9d 100644 --- a/src/routes/components/charts/historicalCostChart/historicalCostChart.tsx +++ b/src/routes/components/charts/historicalCostChart/historicalCostChart.tsx @@ -270,7 +270,7 @@ class HistoricalCostChartBase extends React.Component intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} + title={(datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} /> ), } as any) diff --git a/src/routes/components/charts/historicalTrendChart/historicalTrendChart.tsx b/src/routes/components/charts/historicalTrendChart/historicalTrendChart.tsx index 6d288a19d..9ecff1a4f 100644 --- a/src/routes/components/charts/historicalTrendChart/historicalTrendChart.tsx +++ b/src/routes/components/charts/historicalTrendChart/historicalTrendChart.tsx @@ -260,7 +260,7 @@ class HistoricalTrendChartBase extends React.Component intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} + title={(datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} /> ), } as any) diff --git a/src/routes/components/charts/historicalUsageChart/historicalUsageChart.tsx b/src/routes/components/charts/historicalUsageChart/historicalUsageChart.tsx index 833ccd18f..52c50fa9f 100644 --- a/src/routes/components/charts/historicalUsageChart/historicalUsageChart.tsx +++ b/src/routes/components/charts/historicalUsageChart/historicalUsageChart.tsx @@ -370,7 +370,7 @@ class HistoricalUsageChartBase extends React.Component intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} + title={(datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} /> ), } as any) diff --git a/src/routes/components/charts/trendChart/trendChart.tsx b/src/routes/components/charts/trendChart/trendChart.tsx index 8f6a68a42..12ff15a51 100644 --- a/src/routes/components/charts/trendChart/trendChart.tsx +++ b/src/routes/components/charts/trendChart/trendChart.tsx @@ -348,7 +348,7 @@ class TrendChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} + title={(datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} /> ), } as any) diff --git a/src/routes/components/charts/usageChart/usageChart.tsx b/src/routes/components/charts/usageChart/usageChart.tsx index 0c34b6df4..ff0310716 100644 --- a/src/routes/components/charts/usageChart/usageChart.tsx +++ b/src/routes/components/charts/usageChart/usageChart.tsx @@ -299,7 +299,7 @@ class UsageChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} + title={(datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} /> ), } as any) From 9e9b3335c2433b0977ec4f244edcee84b2468864 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 4 Mar 2024 22:08:49 -0500 Subject: [PATCH 18/61] Remove unused codecov line --- .github/workflows/pull_request.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 92298b9c6..8bee9661a 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -44,6 +44,3 @@ jobs: - name: Test if: ${{ success() }} run: npm test --coverage --maxWorkers=4 - - name: Code coverage - if: ${{ success() }} - uses: codecov/codecov-action@v1 From 39585a723fa07157ed76c4709918116eb7faa3ef Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Tue, 5 Mar 2024 08:53:56 -0500 Subject: [PATCH 19/61] Add hooks for feature flags https://issues.redhat.com/browse/COST-4733 --- src/components/featureFlags/featureFlags.tsx | 69 ++++++++++++++++---- src/components/featureFlags/index.ts | 3 +- src/routes/settings/tagLabels/tagLabels.tsx | 6 +- 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/components/featureFlags/featureFlags.tsx b/src/components/featureFlags/featureFlags.tsx index ca26d5dc1..f218662cc 100644 --- a/src/components/featureFlags/featureFlags.tsx +++ b/src/components/featureFlags/featureFlags.tsx @@ -16,24 +16,67 @@ export const enum FeatureToggle { tagMapping = 'cost-management.ui.tag.mapping', // https://issues.redhat.com/browse/COST-3824 } -// The FeatureFlags component saves feature flags in store for places where Unleash hooks not available -const useFeatureFlags = () => { +const useIsFlagEnabled = (flag: FeatureToggle) => { const client = useUnleashClient(); - const dispatch = useDispatch(); + return client.isEnabled(flag); +}; + +export const useIsClusterInfoFeatureEnabled = () => { + return useIsFlagEnabled(FeatureToggle.clusterInfo); +}; + +export const useIsExportsFeatureEnabled = () => { + return useIsFlagEnabled(FeatureToggle.exports); +}; + +export const useIsFinsightsFeatureEnabled = () => { + return useIsFlagEnabled(FeatureToggle.finsights); +}; + +export const useIsIbmFeatureEnabled = () => { + return useIsFlagEnabled(FeatureToggle.ibm); +}; + +export const useIsRosFeatureEnabled = () => { const { isBeta } = useChrome(); + const isRosFeatureEnabled = useIsFlagEnabled(FeatureToggle.ros); + const isRosFeatureBetaEnabled = useIsFlagEnabled(FeatureToggle.rosBeta) && isBeta(); // Enabled for prod-beta + return isRosFeatureEnabled || isRosFeatureBetaEnabled; +}; + +export const useIsSettingsPlatformFeatureEnabled = () => { + return useIsFlagEnabled(FeatureToggle.settingsPlatform); +}; + +export const useIsTagMappingFeatureEnabled = () => { + return useIsFlagEnabled(FeatureToggle.tagMapping); +}; + +// The FeatureFlags component saves feature flags in store for places where Unleash hooks not available +export const useFeatureFlags = () => { + const dispatch = useDispatch(); + + const isClusterInfoFeatureEnabled = useIsClusterInfoFeatureEnabled(); + const isExportsFeatureEnabled = useIsExportsFeatureEnabled(); + const isFinsightsFeatureEnabled = useIsFinsightsFeatureEnabled(); + const isIbmFeatureEnabled = useIsIbmFeatureEnabled(); + const isRosFeatureEnabled = useIsRosFeatureEnabled(); + const isSettingsPlatformFeatureEnabled = useIsSettingsPlatformFeatureEnabled(); + const isTagMappingFeatureEnabled = useIsTagMappingFeatureEnabled(); useLayoutEffect(() => { // Workaround for code that doesn't use hooks - const flags = { - isClusterInfoFeatureEnabled: client.isEnabled(FeatureToggle.clusterInfo), - isExportsFeatureEnabled: client.isEnabled(FeatureToggle.exports), - isFinsightsFeatureEnabled: client.isEnabled(FeatureToggle.finsights), - isIbmFeatureEnabled: client.isEnabled(FeatureToggle.ibm), - isRosFeatureEnabled: client.isEnabled(FeatureToggle.ros) || (client.isEnabled(FeatureToggle.rosBeta) && isBeta()), // Need to check beta in prod - isSettingsPlatformFeatureEnabled: client.isEnabled(FeatureToggle.settingsPlatform), - isTagMappingFeatureEnabled: client.isEnabled(FeatureToggle.tagMapping), - }; - dispatch(featureFlagsActions.setFeatureFlags(flags)); + dispatch( + featureFlagsActions.setFeatureFlags({ + isClusterInfoFeatureEnabled, + isExportsFeatureEnabled, + isFinsightsFeatureEnabled, + isIbmFeatureEnabled, + isRosFeatureEnabled, + isSettingsPlatformFeatureEnabled, + isTagMappingFeatureEnabled, + }) + ); }); }; diff --git a/src/components/featureFlags/index.ts b/src/components/featureFlags/index.ts index fff73107b..03226b072 100644 --- a/src/components/featureFlags/index.ts +++ b/src/components/featureFlags/index.ts @@ -1 +1,2 @@ -export { default as useFeatureFlags, FeatureToggle } from './featureFlags'; +export * from './featureFlags'; +export { default as useFeatureFlags } from './featureFlags'; diff --git a/src/routes/settings/tagLabels/tagLabels.tsx b/src/routes/settings/tagLabels/tagLabels.tsx index 5e79f22ec..6fa6fad98 100644 --- a/src/routes/settings/tagLabels/tagLabels.tsx +++ b/src/routes/settings/tagLabels/tagLabels.tsx @@ -4,6 +4,7 @@ import { getQuery } from 'api/queries/query'; import type { Settings, SettingsData } from 'api/settings'; import { SettingsType } from 'api/settings'; import type { AxiosError } from 'axios'; +import { useIsTagMappingFeatureEnabled } from 'components/featureFlags'; import messages from 'locales/messages'; import React, { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; @@ -15,7 +16,6 @@ import { NotAvailable } from 'routes/components/page/notAvailable'; import * as queryUtils from 'routes/utils/query'; import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; -import { featureFlagsSelectors } from 'store/featureFlags'; import { settingsActions, settingsSelectors } from 'store/settings'; import { useStateCallback } from 'utils/hooks'; @@ -288,9 +288,7 @@ const useMapToProps = ({ query }: TagLabelsMapProps): TagLabelsStateProps => { }, [query, settingsUpdateDisableStatus, settingsUpdateEnableStatus]); return { - isTagMappingFeatureEnabled: useSelector((state: RootState) => - featureFlagsSelectors.selectIsTagMappingFeatureEnabled(state) - ), + isTagMappingFeatureEnabled: useIsTagMappingFeatureEnabled(), settings, settingsError, settingsStatus, From ce3c2eab40cbcba05c4ab2d81f072f644f5263b9 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Tue, 5 Mar 2024 09:34:08 -0500 Subject: [PATCH 20/61] Dependency updates --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0fd43bc01..75e090db7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,7 +55,7 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", - "@types/react": "^18.2.62", + "@types/react": "^18.2.63", "@types/react-dom": "^18.2.19", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", @@ -4066,9 +4066,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.62", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.62.tgz", - "integrity": "sha512-l3f57BbaEKP0xcFzf+5qRG8/PXykZiuVM6eEoPtqBPCp6dxO3HhDkLIgIyXPhPKNAeXn3KO2pEaNgzaEo/asaw==", + "version": "18.2.63", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.63.tgz", + "integrity": "sha512-ppaqODhs15PYL2nGUOaOu2RSCCB4Difu4UFrP4I3NHLloXC/ESQzQMi9nvjfT1+rudd0d2L3fQPJxRSey+rGlQ==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", diff --git a/package.json b/package.json index 057fa4d33..0067420bd 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", - "@types/react": "^18.2.62", + "@types/react": "^18.2.63", "@types/react-dom": "^18.2.19", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", From 3b9f87e8ae786d0e33738cce57c31e05d91b1f85 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Wed, 6 Mar 2024 11:52:03 -0500 Subject: [PATCH 21/61] Refactor feature flags as feature toggles https://issues.redhat.com/browse/COST-4733 --- src/app.tsx | 6 +- src/components/featureFlags/featureFlags.tsx | 83 ------------- src/components/featureFlags/index.ts | 2 - .../featureToggle/featureToggle.tsx | 110 ++++++++++++++++++ src/components/featureToggle/index.ts | 2 + src/components/permissions/permissions.tsx | 26 ++--- src/routes/components/export/exportModal.tsx | 16 +-- src/routes/components/export/exportSubmit.tsx | 10 +- .../components/perspective/perspective.tsx | 14 +-- .../details/awsDetails/detailsHeader.tsx | 10 +- .../details/azureDetails/detailsHeader.tsx | 10 +- .../components/breakdown/breakdownBase.tsx | 6 +- .../components/breakdown/breakdownHeader.tsx | 10 +- .../details/gcpDetails/detailsHeader.tsx | 10 +- .../details/ibmDetails/detailsHeader.tsx | 10 +- .../details/ociDetails/detailsHeader.tsx | 10 +- .../details/ocpBreakdown/ocpBreakdown.tsx | 4 +- .../details/ocpDetails/detailsHeader.tsx | 10 +- .../details/ocpDetails/detailsTable.tsx | 10 +- src/routes/details/ocpDetails/ocpDetails.tsx | 10 +- .../details/rhelDetails/detailsHeader.tsx | 10 +- src/routes/explorer/explorer.tsx | 10 +- src/routes/explorer/explorerHeader.tsx | 26 ++--- .../components/dashboardWidgetBase.tsx | 6 +- .../ocpDashboard/ocpDashboardWidget.tsx | 4 +- src/routes/overview/overview.tsx | 36 +++--- src/routes/settings/settings.tsx | 4 +- src/routes/settings/tagLabels/tagLabels.tsx | 10 +- src/store/export/exportActions.tsx | 6 +- .../__snapshots__/featureFlags.test.ts.snap | 14 --- src/store/featureFlags/featureFlags.test.ts | 39 ------- src/store/featureFlags/featureFlagsActions.ts | 14 --- src/store/featureFlags/featureFlagsReducer.ts | 51 -------- .../featureFlags/featureFlagsSelectors.ts | 20 ---- src/store/featureFlags/index.ts | 7 -- .../__snapshots__/featureToggle.test.ts.snap | 15 +++ src/store/featureToggle/featureToggle.test.ts | 39 +++++++ .../featureToggle/featureToggleActions.ts | 15 +++ .../featureToggle/featureToggleReducer.ts | 54 +++++++++ .../featureToggle/featureToggleSelectors.ts | 21 ++++ src/store/featureToggle/index.ts | 7 ++ src/store/rootReducer.ts | 4 +- 42 files changed, 407 insertions(+), 374 deletions(-) delete mode 100644 src/components/featureFlags/featureFlags.tsx delete mode 100644 src/components/featureFlags/index.ts create mode 100644 src/components/featureToggle/featureToggle.tsx create mode 100644 src/components/featureToggle/index.ts delete mode 100644 src/store/featureFlags/__snapshots__/featureFlags.test.ts.snap delete mode 100644 src/store/featureFlags/featureFlags.test.ts delete mode 100644 src/store/featureFlags/featureFlagsActions.ts delete mode 100644 src/store/featureFlags/featureFlagsReducer.ts delete mode 100644 src/store/featureFlags/featureFlagsSelectors.ts delete mode 100644 src/store/featureFlags/index.ts create mode 100644 src/store/featureToggle/__snapshots__/featureToggle.test.ts.snap create mode 100644 src/store/featureToggle/featureToggle.test.ts create mode 100644 src/store/featureToggle/featureToggleActions.ts create mode 100644 src/store/featureToggle/featureToggleReducer.ts create mode 100644 src/store/featureToggle/featureToggleSelectors.ts create mode 100644 src/store/featureToggle/index.ts diff --git a/src/app.tsx b/src/app.tsx index e410f05dd..fec7429a0 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useLayoutEffect } from 'react'; import { invalidateSession } from 'utils/sessionStorage'; import pkg from '../package.json'; -import { useFeatureFlags } from './components/featureFlags'; +import { useFeatureToggle } from './components/featureToggle'; import { Routes } from './routes'; const App = () => { @@ -22,8 +22,8 @@ const App = () => { updateDocumentTitle(pkg.insights.appname); }, []); - // Initialize Unleash feature flags - useFeatureFlags(); + // Initialize Unleash feature toggles + useFeatureToggle(); // Clear local storage value if current session is not valid invalidateSession(); diff --git a/src/components/featureFlags/featureFlags.tsx b/src/components/featureFlags/featureFlags.tsx deleted file mode 100644 index f218662cc..000000000 --- a/src/components/featureFlags/featureFlags.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome'; -import { useUnleashClient } from '@unleash/proxy-client-react'; -import { useLayoutEffect } from 'react'; -import { useDispatch } from 'react-redux'; -import { featureFlagsActions } from 'store/featureFlags'; - -// eslint-disable-next-line no-shadow -export const enum FeatureToggle { - clusterInfo = 'cost-management.ui.cluster.info', // https://issues.redhat.com/browse/COST-4559 - exports = 'cost-management.ui.exports', // Async exports https://issues.redhat.com/browse/COST-2223 - finsights = 'cost-management.ui.finsights', // RHEL support for FINsights https://issues.redhat.com/browse/COST-3306 - ibm = 'cost-management.ui.ibm', // IBM https://issues.redhat.com/browse/COST-935 - ros = 'cost-management.ui.ros', // ROS support https://issues.redhat.com/browse/COST-3477 - rosBeta = 'cost-management.ui.ros-beta', // ROS support https://issues.redhat.com/browse/COST-3477 - settingsPlatform = 'cost-management.ui.settings.platform', // Platform projects https://issues.redhat.com/browse/COST-3818 - tagMapping = 'cost-management.ui.tag.mapping', // https://issues.redhat.com/browse/COST-3824 -} - -const useIsFlagEnabled = (flag: FeatureToggle) => { - const client = useUnleashClient(); - return client.isEnabled(flag); -}; - -export const useIsClusterInfoFeatureEnabled = () => { - return useIsFlagEnabled(FeatureToggle.clusterInfo); -}; - -export const useIsExportsFeatureEnabled = () => { - return useIsFlagEnabled(FeatureToggle.exports); -}; - -export const useIsFinsightsFeatureEnabled = () => { - return useIsFlagEnabled(FeatureToggle.finsights); -}; - -export const useIsIbmFeatureEnabled = () => { - return useIsFlagEnabled(FeatureToggle.ibm); -}; - -export const useIsRosFeatureEnabled = () => { - const { isBeta } = useChrome(); - const isRosFeatureEnabled = useIsFlagEnabled(FeatureToggle.ros); - const isRosFeatureBetaEnabled = useIsFlagEnabled(FeatureToggle.rosBeta) && isBeta(); // Enabled for prod-beta - return isRosFeatureEnabled || isRosFeatureBetaEnabled; -}; - -export const useIsSettingsPlatformFeatureEnabled = () => { - return useIsFlagEnabled(FeatureToggle.settingsPlatform); -}; - -export const useIsTagMappingFeatureEnabled = () => { - return useIsFlagEnabled(FeatureToggle.tagMapping); -}; - -// The FeatureFlags component saves feature flags in store for places where Unleash hooks not available -export const useFeatureFlags = () => { - const dispatch = useDispatch(); - - const isClusterInfoFeatureEnabled = useIsClusterInfoFeatureEnabled(); - const isExportsFeatureEnabled = useIsExportsFeatureEnabled(); - const isFinsightsFeatureEnabled = useIsFinsightsFeatureEnabled(); - const isIbmFeatureEnabled = useIsIbmFeatureEnabled(); - const isRosFeatureEnabled = useIsRosFeatureEnabled(); - const isSettingsPlatformFeatureEnabled = useIsSettingsPlatformFeatureEnabled(); - const isTagMappingFeatureEnabled = useIsTagMappingFeatureEnabled(); - - useLayoutEffect(() => { - // Workaround for code that doesn't use hooks - dispatch( - featureFlagsActions.setFeatureFlags({ - isClusterInfoFeatureEnabled, - isExportsFeatureEnabled, - isFinsightsFeatureEnabled, - isIbmFeatureEnabled, - isRosFeatureEnabled, - isSettingsPlatformFeatureEnabled, - isTagMappingFeatureEnabled, - }) - ); - }); -}; - -export default useFeatureFlags; diff --git a/src/components/featureFlags/index.ts b/src/components/featureFlags/index.ts deleted file mode 100644 index 03226b072..000000000 --- a/src/components/featureFlags/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './featureFlags'; -export { default as useFeatureFlags } from './featureFlags'; diff --git a/src/components/featureToggle/featureToggle.tsx b/src/components/featureToggle/featureToggle.tsx new file mode 100644 index 000000000..5cb1c139a --- /dev/null +++ b/src/components/featureToggle/featureToggle.tsx @@ -0,0 +1,110 @@ +import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome'; +import { useUnleashClient } from '@unleash/proxy-client-react'; +import { useLayoutEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { FeatureToggleActions } from 'store/featureToggle'; + +// eslint-disable-next-line no-shadow +export const enum FeatureToggle { + clusterInfo = 'cost-management.ui.cluster.info', // https://issues.redhat.com/browse/COST-4559 + debug = 'cost-management.ui.debug', + exports = 'cost-management.ui.exports', // Async exports https://issues.redhat.com/browse/COST-2223 + finsights = 'cost-management.ui.finsights', // RHEL support for FINsights https://issues.redhat.com/browse/COST-3306 + ibm = 'cost-management.ui.ibm', // IBM https://issues.redhat.com/browse/COST-935 + ros = 'cost-management.ui.ros', // ROS support https://issues.redhat.com/browse/COST-3477 + rosBeta = 'cost-management.ui.ros-beta', // ROS support https://issues.redhat.com/browse/COST-3477 + settingsPlatform = 'cost-management.ui.settings.platform', // Platform projects https://issues.redhat.com/browse/COST-3818 + tagMapping = 'cost-management.ui.tag.mapping', // https://issues.redhat.com/browse/COST-3824 +} + +const useIsToggleEnabled = (toggle: FeatureToggle) => { + const client = useUnleashClient(); + return client.isEnabled(toggle); +}; + +export const useIsDebugToggleEnabled = () => { + return useIsToggleEnabled(FeatureToggle.debug); +}; + +export const useIsClusterInfoToggleEnabled = () => { + return useIsToggleEnabled(FeatureToggle.clusterInfo); +}; + +export const useIsExportsToggleEnabled = () => { + return useIsToggleEnabled(FeatureToggle.exports); +}; + +export const useIsFinsightsToggleEnabled = () => { + return useIsToggleEnabled(FeatureToggle.finsights); +}; + +export const useIsIbmToggleEnabled = () => { + return useIsToggleEnabled(FeatureToggle.ibm); +}; + +export const useIsRosToggleEnabled = () => { + const { isBeta } = useChrome(); + const isRosToggleEnabled = useIsToggleEnabled(FeatureToggle.ros); + const isRosFeatureBetaEnabled = useIsToggleEnabled(FeatureToggle.rosBeta) && isBeta(); // Enabled for prod-beta + return isRosToggleEnabled || isRosFeatureBetaEnabled; +}; + +export const useIsSettingsPlatformToggleEnabled = () => { + return useIsToggleEnabled(FeatureToggle.settingsPlatform); +}; + +export const useIsTagMappingToggleEnabled = () => { + return useIsToggleEnabled(FeatureToggle.tagMapping); +}; + +// The FeatureToggle component saves feature toggles in store for places where Unleash hooks not available +export const useFeatureToggle = () => { + const dispatch = useDispatch(); + const { auth } = useChrome(); + + const isClusterInfoToggleEnabled = useIsClusterInfoToggleEnabled(); + const isDebugToggleEnabled = useIsDebugToggleEnabled(); + const isExportsToggleEnabled = useIsExportsToggleEnabled(); + const isFinsightsToggleEnabled = useIsFinsightsToggleEnabled(); + const isIbmToggleEnabled = useIsIbmToggleEnabled(); + const isRosToggleEnabled = useIsRosToggleEnabled(); + const isSettingsPlatformToggleEnabled = useIsSettingsPlatformToggleEnabled(); + const isTagMappingToggleEnabled = useIsTagMappingToggleEnabled(); + + const fetchUser = callback => { + auth.getUser().then(user => { + callback((user as any).identity); + }); + }; + + useLayoutEffect(() => { + // Workaround for code that doesn't use hooks + dispatch( + FeatureToggleActions.setFeatureToggle({ + isClusterInfoToggleEnabled, + isDebugToggleEnabled, + isExportsToggleEnabled, + isFinsightsToggleEnabled, + isIbmToggleEnabled, + isRosToggleEnabled, + isSettingsPlatformToggleEnabled, + isTagMappingToggleEnabled, + }) + ); + if (isDebugToggleEnabled) { + // eslint-disable-next-line no-console + fetchUser(identity => console.log('User identity:', identity)); + } + }, [ + isClusterInfoToggleEnabled, + isDebugToggleEnabled, + isExportsToggleEnabled, + isFinsightsToggleEnabled, + isIbmToggleEnabled, + isRosToggleEnabled, + isSettingsPlatformToggleEnabled, + isTagMappingToggleEnabled, + ]); +}; + +export default useFeatureToggle; diff --git a/src/components/featureToggle/index.ts b/src/components/featureToggle/index.ts new file mode 100644 index 000000000..86f8a50c7 --- /dev/null +++ b/src/components/featureToggle/index.ts @@ -0,0 +1,2 @@ +export * from './featureToggle'; +export { default as useFeatureToggle } from './featureToggle'; diff --git a/src/components/permissions/permissions.tsx b/src/components/permissions/permissions.tsx index 02802657c..3ad91ede1 100644 --- a/src/components/permissions/permissions.tsx +++ b/src/components/permissions/permissions.tsx @@ -9,7 +9,7 @@ import { Loading } from 'routes/components/page/loading'; import { NotAuthorized } from 'routes/components/page/notAuthorized'; import { NotAvailable } from 'routes/components/page/notAvailable'; import { createMapStateToProps, FetchStatus } from 'store/common'; -import { featureFlagsSelectors } from 'store/featureFlags'; +import { FeatureToggleSelectors } from 'store/featureToggle'; import { userAccessQuery, userAccessSelectors } from 'store/userAccess'; import type { ChromeComponentProps } from 'utils/chrome'; import { withChrome } from 'utils/chrome'; @@ -32,9 +32,9 @@ interface PermissionsOwnProps extends ChromeComponentProps { } interface PermissionsStateProps { - isFinsightsFeatureEnabled?: boolean; - isIbmFeatureEnabled?: boolean; - isRosFeatureEnabled?: boolean; + isFinsightsToggleEnabled?: boolean; + isIbmToggleEnabled?: boolean; + isRosToggleEnabled?: boolean; userAccess: UserAccess; userAccessError: AxiosError; userAccessFetchStatus: FetchStatus; @@ -46,9 +46,9 @@ type PermissionsProps = PermissionsOwnProps & PermissionsStateProps; const PermissionsBase: React.FC = ({ children = null, // chrome, - isFinsightsFeatureEnabled, - isIbmFeatureEnabled, - isRosFeatureEnabled, + isFinsightsToggleEnabled, + isIbmToggleEnabled, + isRosToggleEnabled, userAccess, userAccessError, userAccessFetchStatus, @@ -62,11 +62,11 @@ const PermissionsBase: React.FC = ({ const azure = hasAzureAccess(userAccess); const costModel = hasCostModelAccess(userAccess); const gcp = hasGcpAccess(userAccess); - const ibm = isIbmFeatureEnabled && hasIbmAccess(userAccess); + const ibm = isIbmToggleEnabled && hasIbmAccess(userAccess); const oci = hasOciAccess(userAccess); const ocp = hasOcpAccess(userAccess); - const rhel = isFinsightsFeatureEnabled && hasRhelAccess(userAccess); - const ros = isRosFeatureEnabled && hasRosAccess(userAccess); + const rhel = isFinsightsToggleEnabled && hasRhelAccess(userAccess); + const ros = isRosToggleEnabled && hasRosAccess(userAccess); const settings = costModel || hasSettingsAccess(userAccess); switch (pathname) { @@ -133,9 +133,9 @@ const mapStateToProps = createMapStateToProps {error && }
- {isExportsFeatureEnabled ? ( + {isExportsToggleEnabled ? ( {intl.formatMessage(messages.exportDesc, { value: {intl.formatMessage(messages.exportsTitle)} })} @@ -227,7 +227,7 @@ export class ExportModalBase extends React.Component
- {isExportsFeatureEnabled && ( + {isExportsToggleEnabled && ( )} - {showFormatType && isExportsFeatureEnabled && ( + {showFormatType && isExportsToggleEnabled && ( {formatTypeOptions.map((option, index) => ( @@ -328,7 +328,7 @@ export class ExportModalBase extends React.Component(state => { return { - isExportsFeatureEnabled: featureFlagsSelectors.selectIsExportsFeatureEnabled(state), + isExportsToggleEnabled: FeatureToggleSelectors.selectIsExportsToggleEnabled(state), }; }); diff --git a/src/routes/components/export/exportSubmit.tsx b/src/routes/components/export/exportSubmit.tsx index 81ecab6a4..a01d4ecbd 100644 --- a/src/routes/components/export/exportSubmit.tsx +++ b/src/routes/components/export/exportSubmit.tsx @@ -17,7 +17,7 @@ import type { ComputedReportItem } from 'routes/utils/computedReport/getComputed import { getDateRangeFromQuery } from 'routes/utils/dateRange'; import { createMapStateToProps, FetchStatus } from 'store/common'; import { exportActions, exportSelectors } from 'store/export'; -import { featureFlagsSelectors } from 'store/featureFlags'; +import { FeatureToggleSelectors } from 'store/featureToggle'; import { getToday } from 'utils/dates'; import { orgUnitIdKey, tagPrefix } from 'utils/props'; import type { RouterComponentProps } from 'utils/router'; @@ -48,7 +48,7 @@ interface ExportSubmitStateProps { exportFetchStatus?: FetchStatus; exportQueryString: string; exportReport: Export; - isExportsFeatureEnabled?: boolean; + isExportsToggleEnabled?: boolean; startDate: string; } @@ -83,9 +83,9 @@ export class ExportSubmitBase extends React.Component { - const { exportQueryString, fetchExport, isExportsFeatureEnabled, reportPathsType } = this.props; + const { exportQueryString, fetchExport, isExportsToggleEnabled, reportPathsType } = this.props; - fetchExport(reportPathsType, reportType, exportQueryString, isExportsFeatureEnabled); + fetchExport(reportPathsType, reportType, exportQueryString, isExportsToggleEnabled); this.setState( { @@ -244,7 +244,7 @@ const mapStateToProps = createMapStateToProps void; @@ -70,7 +70,7 @@ const getInfrastructureOptions = ({ hasIbm, hasIbmOcp, hasOci, - isIbmFeatureEnabled, + isIbmToggleEnabled, }) => { const options = []; @@ -89,7 +89,7 @@ const getInfrastructureOptions = ({ if (hasIbm) { options.push(...infrastructureIbmOptions); } - if (hasIbmOcp && isIbmFeatureEnabled) { + if (hasIbmOcp && isIbmToggleEnabled) { options.push(...infrastructureIbmOcpOptions); } if (hasAzure) { @@ -119,7 +119,7 @@ const Perspective: React.FC = ({ hasOcpCloud, hasRhel, isDisabled, - isIbmFeatureEnabled, + isIbmToggleEnabled, isInfrastructureTab, isRhelTab, onSelect, @@ -144,7 +144,7 @@ const Perspective: React.FC = ({ hasIbm, hasIbmOcp, hasOci, - isIbmFeatureEnabled, + isIbmToggleEnabled, }) ); } else if (isRhelTab) { @@ -175,7 +175,7 @@ const Perspective: React.FC = ({ hasIbm, hasIbmOcp, hasOci, - isIbmFeatureEnabled, + isIbmToggleEnabled, }) ); } diff --git a/src/routes/details/awsDetails/detailsHeader.tsx b/src/routes/details/awsDetails/detailsHeader.tsx index 71200ed90..0e2aef4a5 100644 --- a/src/routes/details/awsDetails/detailsHeader.tsx +++ b/src/routes/details/awsDetails/detailsHeader.tsx @@ -21,7 +21,7 @@ import { getIdKeyForGroupBy } from 'routes/utils/computedReport/getComputedAwsRe import { filterProviders } from 'routes/utils/providers'; import type { FetchStatus } from 'store/common'; import { createMapStateToProps } from 'store/common'; -import { featureFlagsSelectors } from 'store/featureFlags'; +import { FeatureToggleSelectors } from 'store/featureToggle'; import { providersQuery, providersSelectors } from 'store/providers'; import { getSinceDateRangeString } from 'utils/dates'; import { formatCurrency } from 'utils/format'; @@ -39,7 +39,7 @@ interface DetailsHeaderOwnProps { } interface DetailsHeaderStateProps { - isExportsFeatureEnabled?: boolean; + isExportsToggleEnabled?: boolean; providers: Providers; providersError: AxiosError; providersFetchStatus: FetchStatus; @@ -75,7 +75,7 @@ class DetailsHeaderBase extends React.Component { costType, currency, groupBy, - isExportsFeatureEnabled, + isExportsToggleEnabled, onCurrencySelect, onGroupBySelect, providers, @@ -95,7 +95,7 @@ class DetailsHeaderBase extends React.Component {
- {isExportsFeatureEnabled && } + {isExportsToggleEnabled && }
@@ -146,7 +146,7 @@ const mapStateToProps = createMapStateToProps { const { currency, groupBy, - isExportsFeatureEnabled, + isExportsToggleEnabled, onCurrencySelect, onGroupBySelect, providers, @@ -79,7 +79,7 @@ class DetailsHeaderBase extends React.Component {
- {isExportsFeatureEnabled && } + {isExportsToggleEnabled && }
@@ -123,7 +123,7 @@ const mapStateToProps = createMapStateToProps { } private getAvailableTabs = () => { - const { costOverviewComponent, historicalDataComponent, isRosFeatureEnabled, optimizationsComponent } = this.props; + const { costOverviewComponent, historicalDataComponent, isRosToggleEnabled, optimizationsComponent } = this.props; const availableTabs = []; if (costOverviewComponent) { @@ -140,7 +140,7 @@ class BreakdownBase extends React.Component { tab: BreakdownTab.historicalData, }); } - if (optimizationsComponent && isRosFeatureEnabled) { + if (optimizationsComponent && isRosToggleEnabled) { availableTabs.push({ contentRef: React.createRef(), showBadge: true, diff --git a/src/routes/details/components/breakdown/breakdownHeader.tsx b/src/routes/details/components/breakdown/breakdownHeader.tsx index 99981c7a2..69eacc5ef 100644 --- a/src/routes/details/components/breakdown/breakdownHeader.tsx +++ b/src/routes/details/components/breakdown/breakdownHeader.tsx @@ -18,7 +18,7 @@ import { Currency } from 'routes/components/currency'; import { TagLink } from 'routes/details/components/tag'; import { getGroupByCostCategory, getGroupByOrgValue, getGroupByTagKey } from 'routes/utils/groupBy'; import { createMapStateToProps } from 'store/common'; -import { featureFlagsSelectors } from 'store/featureFlags'; +import { FeatureToggleSelectors } from 'store/featureToggle'; import { getTotalCostDateRangeString } from 'utils/dates'; import { formatCurrency } from 'utils/format'; import { awsCategoryKey, orgUnitIdKey, tagKey } from 'utils/props'; @@ -50,7 +50,7 @@ interface BreakdownHeaderOwnProps extends RouterComponentProps { } interface BreakdownHeaderStateProps { - isClusterInfoFeatureEnabled?: boolean; + isClusterInfoToggleEnabled?: boolean; } interface BreakdownHeaderDispatchProps { @@ -98,7 +98,7 @@ class BreakdownHeader extends React.Component { description, groupBy, intl, - isClusterInfoFeatureEnabled, + isClusterInfoToggleEnabled, onCostDistributionSelect, onCostTypeSelect, onCurrencySelect, @@ -160,7 +160,7 @@ class BreakdownHeader extends React.Component { {description && (
{description} - {clusterInfoComponent && isClusterInfoFeatureEnabled ? clusterInfoComponent : null} + {clusterInfoComponent && isClusterInfoToggleEnabled ? clusterInfoComponent : null}
)} @@ -201,7 +201,7 @@ class BreakdownHeader extends React.Component { const mapStateToProps = createMapStateToProps(state => { return { - isClusterInfoFeatureEnabled: featureFlagsSelectors.selectIsClusterInfoFeatureEnabled(state), + isClusterInfoToggleEnabled: FeatureToggleSelectors.selectIsClusterInfoToggleEnabled(state), }; }); diff --git a/src/routes/details/gcpDetails/detailsHeader.tsx b/src/routes/details/gcpDetails/detailsHeader.tsx index b30c6a39c..b7a7adc8e 100644 --- a/src/routes/details/gcpDetails/detailsHeader.tsx +++ b/src/routes/details/gcpDetails/detailsHeader.tsx @@ -18,7 +18,7 @@ import { getIdKeyForGroupBy } from 'routes/utils/computedReport/getComputedGcpRe import { filterProviders } from 'routes/utils/providers'; import type { FetchStatus } from 'store/common'; import { createMapStateToProps } from 'store/common'; -import { featureFlagsSelectors } from 'store/featureFlags'; +import { FeatureToggleSelectors } from 'store/featureToggle'; import { providersQuery, providersSelectors } from 'store/providers'; import { getSinceDateRangeString } from 'utils/dates'; import { formatCurrency } from 'utils/format'; @@ -34,7 +34,7 @@ interface DetailsHeaderOwnProps { } interface DetailsHeaderStateProps { - isExportsFeatureEnabled?: boolean; + isExportsToggleEnabled?: boolean; providers: Providers; providersError: AxiosError; providersFetchStatus: FetchStatus; @@ -60,7 +60,7 @@ class DetailsHeaderBase extends React.Component { const { currency, groupBy, - isExportsFeatureEnabled, + isExportsToggleEnabled, onCurrencySelect, onGroupBySelect, providers, @@ -80,7 +80,7 @@ class DetailsHeaderBase extends React.Component {
- {isExportsFeatureEnabled && } + {isExportsToggleEnabled && }
@@ -124,7 +124,7 @@ const mapStateToProps = createMapStateToProps { const { currency, groupBy, - isExportsFeatureEnabled, + isExportsToggleEnabled, onCurrencySelect, onGroupBySelect, providers, @@ -80,7 +80,7 @@ class DetailsHeaderBase extends React.Component {
- {isExportsFeatureEnabled && } + {isExportsToggleEnabled && }
@@ -124,7 +124,7 @@ const mapStateToProps = createMapStateToProps { const { currency, groupBy, - isExportsFeatureEnabled, + isExportsToggleEnabled, onCurrencySelect, onGroupBySelect, providers, @@ -79,7 +79,7 @@ class DetailsHeaderBase extends React.Component {
- {isExportsFeatureEnabled && } + {isExportsToggleEnabled && }
@@ -123,7 +123,7 @@ const mapStateToProps = createMapStateToProps, isOptimizationsTab: queryFromRoute.optimizationsTab !== undefined, - isRosFeatureEnabled: featureFlagsSelectors.selectIsRosFeatureEnabled(state), + isRosToggleEnabled: FeatureToggleSelectors.selectIsRosToggleEnabled(state), optimizationsComponent: groupBy === 'project' && groupByValue !== '*' ? : undefined, providers: filterProviders(providers, ProviderType.ocp), providersFetchStatus, diff --git a/src/routes/details/ocpDetails/detailsHeader.tsx b/src/routes/details/ocpDetails/detailsHeader.tsx index 8df28f0c5..ff5daf412 100644 --- a/src/routes/details/ocpDetails/detailsHeader.tsx +++ b/src/routes/details/ocpDetails/detailsHeader.tsx @@ -21,7 +21,7 @@ import { getIdKeyForGroupBy } from 'routes/utils/computedReport/getComputedOcpRe import { filterProviders } from 'routes/utils/providers'; import type { FetchStatus } from 'store/common'; import { createMapStateToProps } from 'store/common'; -import { featureFlagsSelectors } from 'store/featureFlags'; +import { FeatureToggleSelectors } from 'store/featureToggle'; import { providersQuery, providersSelectors } from 'store/providers'; import { getSinceDateRangeString } from 'utils/dates'; import { formatCurrency } from 'utils/format'; @@ -39,7 +39,7 @@ interface DetailsHeaderOwnProps { } interface DetailsHeaderStateProps { - isExportsFeatureEnabled?: boolean; + isExportsToggleEnabled?: boolean; providers: Providers; providersError: AxiosError; providersFetchStatus: FetchStatus; @@ -70,7 +70,7 @@ class DetailsHeaderBase extends React.Component
- {isExportsFeatureEnabled && } + {isExportsToggleEnabled && }
@@ -167,7 +167,7 @@ const mapStateToProps = createMapStateToProps; isAllSelected?: boolean; isLoading?: boolean; - isRosFeatureEnabled?: boolean; + isRosToggleEnabled?: boolean; onSelect(items: ComputedReportItem[], isSelected: boolean); onSort(value: string, isSortAscending: boolean); orderBy?: any; @@ -95,7 +95,7 @@ class DetailsTableBase extends React.Component { }; private getTable = () => { - const { costDistribution, isRosFeatureEnabled, query, report, reportFetchStatus, reportQueryString, router } = + const { costDistribution, isRosToggleEnabled, query, report, reportFetchStatus, reportQueryString, router } = this.props; const { hiddenColumns, isAllSelected, selectedItems } = this.state; @@ -266,7 +266,7 @@ class OcpDetails extends React.Component { hiddenColumns={hiddenColumns} isAllSelected={isAllSelected} isLoading={reportFetchStatus === FetchStatus.inProgress} - isRosFeatureEnabled={isRosFeatureEnabled} + isRosToggleEnabled={isRosToggleEnabled} onSelect={this.handleOnSelect} onSort={(sortType, isSortAscending) => handleOnSort(query, router, sortType, isSortAscending)} orderBy={query.order_by} @@ -519,7 +519,7 @@ const mapStateToProps = createMapStateToProps { const { currency, groupBy, - isExportsFeatureEnabled, + isExportsToggleEnabled, onCurrencySelect, onGroupBySelect, providers, @@ -105,7 +105,7 @@ class DetailsHeaderBase extends React.Component {
- {isExportsFeatureEnabled && } + {isExportsToggleEnabled && }
@@ -154,7 +154,7 @@ const mapStateToProps = createMapStateToProps { }; private isRhelAvailable = () => { - const { isFinsightsFeatureEnabled, rhelProviders, userAccess } = this.props; - return isFinsightsFeatureEnabled && isRhelAvailable(userAccess, rhelProviders); + const { isFinsightsToggleEnabled, rhelProviders, userAccess } = this.props; + return isFinsightsToggleEnabled && isRhelAvailable(userAccess, rhelProviders); }; private updateReport = () => { @@ -664,7 +664,7 @@ const mapStateToProps = createMapStateToProps { - const { isIbmFeatureEnabled } = this.props; + const { isIbmToggleEnabled } = this.props; const { currentPerspective } = this.state; const hasAws = this.isAwsAvailable(); @@ -158,7 +158,7 @@ class ExplorerHeaderBase extends React.Component ); @@ -243,8 +243,8 @@ class ExplorerHeaderBase extends React.Component { - const { isFinsightsFeatureEnabled, rhelProviders, userAccess } = this.props; - return isFinsightsFeatureEnabled && isRhelAvailable(userAccess, rhelProviders); + const { isFinsightsToggleEnabled, rhelProviders, userAccess } = this.props; + return isFinsightsToggleEnabled && isRhelAvailable(userAccess, rhelProviders); }; public render() { @@ -254,7 +254,7 @@ class ExplorerHeaderBase extends React.Component
- {isExportsFeatureEnabled && } + {isExportsToggleEnabled && }
@@ -379,9 +379,9 @@ const mapStateToProps = createMapStateToProps string; - isRosFeatureEnabled?: boolean; + isRosToggleEnabled?: boolean; previousReport?: Report; previousReportError?: AxiosError; previousReportFetchStatus?: number; @@ -573,9 +573,9 @@ class DashboardWidgetBase extends React.Component { } private getAvailableTabs = () => { - const { isFinsightsFeatureEnabled } = this.props; + const { isFinsightsToggleEnabled } = this.props; const availableTabs = []; const infrastructureTabs = @@ -230,7 +230,7 @@ class OverviewBase extends React.Component { ] : undefined; - if (isFinsightsFeatureEnabled) { + if (isFinsightsToggleEnabled) { if (infrastructureTabs) { availableTabs.push(...infrastructureTabs); } @@ -276,7 +276,7 @@ class OverviewBase extends React.Component { }; private getCurrentTab = () => { - const { isFinsightsFeatureEnabled } = this.props; + const { isFinsightsToggleEnabled } = this.props; const { activeTabKey } = this.state; const hasAws = this.isAwsAvailable(); @@ -300,7 +300,7 @@ class OverviewBase extends React.Component { } else if (showRhelOnly) { return OverviewTab.rhel; } else { - if (isFinsightsFeatureEnabled) { + if (isFinsightsToggleEnabled) { switch (activeTabKey) { case 0: return OverviewTab.infrastructure; @@ -387,7 +387,7 @@ class OverviewBase extends React.Component { }; private getPerspective = () => { - const { isIbmFeatureEnabled } = this.props; + const { isIbmToggleEnabled } = this.props; const { currentInfrastructurePerspective, currentOcpPerspective, currentRhelPerspective } = this.state; const hasAws = this.isAwsAvailable(); @@ -432,7 +432,7 @@ class OverviewBase extends React.Component { hasOcp={hasOcp} hasOcpCloud={this.isOcpCloudAvailable()} hasRhel={hasRhel} - isIbmFeatureEnabled={isIbmFeatureEnabled} + isIbmToggleEnabled={isIbmToggleEnabled} isInfrastructureTab={OverviewTab.infrastructure === currentTab} isRhelTab={OverviewTab.rhel === currentTab} onSelect={this.handleOnPerspectiveSelect} @@ -565,10 +565,10 @@ class OverviewBase extends React.Component { }; private getTabTitle = (tab: OverviewTab) => { - const { intl, isFinsightsFeatureEnabled } = this.props; + const { intl, isFinsightsToggleEnabled } = this.props; if (tab === OverviewTab.infrastructure) { - if (isFinsightsFeatureEnabled) { + if (isFinsightsToggleEnabled) { return intl.formatMessage(messages.summary); } return intl.formatMessage(messages.infrastructure); @@ -698,12 +698,12 @@ class OverviewBase extends React.Component { }; private isRhelAvailable = () => { - const { isFinsightsFeatureEnabled, rhelProviders, userAccess } = this.props; - return isFinsightsFeatureEnabled && isRhelAvailable(userAccess, rhelProviders); + const { isFinsightsToggleEnabled, rhelProviders, userAccess } = this.props; + return isFinsightsToggleEnabled && isRhelAvailable(userAccess, rhelProviders); }; public render() { - const { providersFetchStatus, intl, isFinsightsFeatureEnabled, isIbmFeatureEnabled, userAccessFetchStatus } = + const { providersFetchStatus, intl, isFinsightsToggleEnabled, isIbmToggleEnabled, userAccessFetchStatus } = this.props; // Note: No need to test OCP on cloud here, since that requires at least one provider @@ -746,7 +746,7 @@ class OverviewBase extends React.Component {

{intl.formatMessage(messages.openShift)}

{intl.formatMessage(messages.openShiftDesc)}


- {isFinsightsFeatureEnabled && ( + {isFinsightsToggleEnabled && ( <>

{intl.formatMessage(messages.rhel)}

{intl.formatMessage(messages.rhelDesc)}

@@ -758,7 +758,7 @@ class OverviewBase extends React.Component {

{intl.formatMessage(messages.gcp)}

{intl.formatMessage(messages.gcpDesc)}

- {isIbmFeatureEnabled && ( + {isIbmToggleEnabled && ( <>

{intl.formatMessage(messages.ibm)}

@@ -840,8 +840,8 @@ const mapStateToProps = createMapStateToProps { return { isSettingsPlatformEnabled: useSelector((state: RootState) => - featureFlagsSelectors.selectIsSettingsPlatformFeatureEnabled(state) + FeatureToggleSelectors.selectIsSettingsPlatformToggleEnabled(state) ), userAccess, userAccessError, diff --git a/src/routes/settings/tagLabels/tagLabels.tsx b/src/routes/settings/tagLabels/tagLabels.tsx index 6fa6fad98..53b10e0fa 100644 --- a/src/routes/settings/tagLabels/tagLabels.tsx +++ b/src/routes/settings/tagLabels/tagLabels.tsx @@ -4,7 +4,7 @@ import { getQuery } from 'api/queries/query'; import type { Settings, SettingsData } from 'api/settings'; import { SettingsType } from 'api/settings'; import type { AxiosError } from 'axios'; -import { useIsTagMappingFeatureEnabled } from 'components/featureFlags'; +import { useIsTagMappingToggleEnabled } from 'components/featureToggle'; import messages from 'locales/messages'; import React, { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; @@ -32,7 +32,7 @@ export interface TagLabelsMapProps { } export interface TagLabelsStateProps { - isTagMappingFeatureEnabled?: boolean; + isTagMappingToggleEnabled?: boolean; settings?: Settings; settingsError?: AxiosError; settingsStatus?: FetchStatus; @@ -56,7 +56,7 @@ const TagLabels: React.FC = ({ canWrite }) => { const dispatch: ThunkDispatch = useDispatch(); const intl = useIntl(); - const { isTagMappingFeatureEnabled, settings, settingsError, settingsStatus } = useMapToProps({ query }); + const { isTagMappingToggleEnabled, settings, settingsError, settingsStatus } = useMapToProps({ query }); const getTags = () => { if (settings) { @@ -241,7 +241,7 @@ const TagLabels: React.FC = ({ canWrite }) => { <> {getTable()}
{getPagination(isDisabled, true)}
- {isTagMappingFeatureEnabled && TEST} + {isTagMappingToggleEnabled && TEST} )} @@ -288,7 +288,7 @@ const useMapToProps = ({ query }: TagLabelsMapProps): TagLabelsStateProps => { }, [query, settingsUpdateDisableStatus, settingsUpdateEnableStatus]); return { - isTagMappingFeatureEnabled: useIsTagMappingFeatureEnabled(), + isTagMappingToggleEnabled: useIsTagMappingToggleEnabled(), settings, settingsError, settingsStatus, diff --git a/src/store/export/exportActions.tsx b/src/store/export/exportActions.tsx index 1a71307d7..12bead39d 100644 --- a/src/store/export/exportActions.tsx +++ b/src/store/export/exportActions.tsx @@ -31,7 +31,7 @@ export function fetchExport( reportPathsType: ReportPathsType, reportType: ReportType, reportQueryString: string, - isExportsFeatureEnabled: boolean = false + isExportsToggleEnabled: boolean = false ): ThunkAction { return (dispatch, getState) => { if (!isExportExpired(getState(), reportPathsType, reportType, reportQueryString)) { @@ -47,7 +47,7 @@ export function fetchExport( .then(res => { dispatch(fetchExportSuccess(res.data, meta)); - if (isExportsFeatureEnabled) { + if (isExportsToggleEnabled) { const description = intl.formatMessage(messages.exportsSuccessDesc, { link: dispatch(removeNotification(exportSuccessID))} />, value: {intl.formatMessage(messages.exportsTitle)}, @@ -67,7 +67,7 @@ export function fetchExport( .catch(err => { dispatch(fetchExportFailure(err, meta)); - if (isExportsFeatureEnabled) { + if (isExportsToggleEnabled) { dispatch( addNotification({ description: intl.formatMessage(messages.exportsFailedDesc), diff --git a/src/store/featureFlags/__snapshots__/featureFlags.test.ts.snap b/src/store/featureFlags/__snapshots__/featureFlags.test.ts.snap deleted file mode 100644 index 4c14bbea0..000000000 --- a/src/store/featureFlags/__snapshots__/featureFlags.test.ts.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`default state 1`] = ` -{ - "hasFeatureFlags": false, - "isClusterInfoFeatureEnabled": false, - "isExportsFeatureEnabled": false, - "isFinsightsFeatureEnabled": false, - "isIbmFeatureEnabled": false, - "isRosFeatureEnabled": false, - "isSettingsPlatformFeatureEnabled": false, - "isTagMappingFeatureEnabled": false, -} -`; diff --git a/src/store/featureFlags/featureFlags.test.ts b/src/store/featureFlags/featureFlags.test.ts deleted file mode 100644 index e5f3dc946..000000000 --- a/src/store/featureFlags/featureFlags.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { createMockStoreCreator } from 'store/mockStore'; - -import { featureFlagsSelectors } from '.'; -import * as actions from './featureFlagsActions'; -import { featureFlagsReducer, stateKey } from './featureFlagsReducer'; -import * as selectors from './featureFlagsSelectors'; - -const createUIStore = createMockStoreCreator({ - [stateKey]: featureFlagsReducer, -}); - -test('default state', async () => { - const store = createUIStore(); - expect(selectors.selectFeatureFlagsState(store.getState())).toMatchSnapshot(); -}); - -test('Exports feature is enabled', async () => { - const store = createUIStore(); - store.dispatch(actions.setFeatureFlags({ isExportsFeatureEnabled: true })); - expect(featureFlagsSelectors.selectIsExportsFeatureEnabled(store.getState())).toBe(true); -}); - -test('FINsights feature is enabled', async () => { - const store = createUIStore(); - store.dispatch(actions.setFeatureFlags({ isFinsightsFeatureEnabled: true })); - expect(featureFlagsSelectors.selectIsFinsightsFeatureEnabled(store.getState())).toBe(true); -}); - -test('IBM feature is enabled', async () => { - const store = createUIStore(); - store.dispatch(actions.setFeatureFlags({ isIbmFeatureEnabled: true })); - expect(featureFlagsSelectors.selectIsIbmFeatureEnabled(store.getState())).toBe(true); -}); - -test('ROS feature is enabled', async () => { - const store = createUIStore(); - store.dispatch(actions.setFeatureFlags({ isRosFeatureEnabled: true })); - expect(featureFlagsSelectors.selectIsRosFeatureEnabled(store.getState())).toBe(true); -}); diff --git a/src/store/featureFlags/featureFlagsActions.ts b/src/store/featureFlags/featureFlagsActions.ts deleted file mode 100644 index a9c02af5b..000000000 --- a/src/store/featureFlags/featureFlagsActions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createAction } from 'typesafe-actions'; - -export interface FeatureFlagsActionMeta { - isClusterInfoFeatureEnabled?: boolean; - isExportsFeatureEnabled?: boolean; - isFinsightsFeatureEnabled?: boolean; - isIbmFeatureEnabled?: boolean; - isRosFeatureEnabled?: boolean; - isSettingsPlatformFeatureEnabled?: boolean; - isTagMappingFeatureEnabled?: boolean; -} - -export const setFeatureFlags = createAction('feature/init_feature_flags')(); -export const resetState = createAction('feature/reset_state')(); diff --git a/src/store/featureFlags/featureFlagsReducer.ts b/src/store/featureFlags/featureFlagsReducer.ts deleted file mode 100644 index 9c077be88..000000000 --- a/src/store/featureFlags/featureFlagsReducer.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { ActionType } from 'typesafe-actions'; -import { getType } from 'typesafe-actions'; - -import type { resetState } from './featureFlagsActions'; -import { setFeatureFlags } from './featureFlagsActions'; - -export type FeatureFlagsAction = ActionType; - -export type FeatureFlagsState = Readonly<{ - hasFeatureFlags: boolean; - isClusterInfoFeatureEnabled: boolean; - isExportsFeatureEnabled: boolean; - isFinsightsFeatureEnabled: boolean; - isIbmFeatureEnabled: boolean; - isRosFeatureEnabled: boolean; - isSettingsPlatformFeatureEnabled: boolean; - isTagMappingFeatureEnabled: boolean; -}>; - -export const defaultState: FeatureFlagsState = { - hasFeatureFlags: false, - isClusterInfoFeatureEnabled: false, - isExportsFeatureEnabled: false, - isFinsightsFeatureEnabled: false, - isIbmFeatureEnabled: false, - isRosFeatureEnabled: false, - isSettingsPlatformFeatureEnabled: false, - isTagMappingFeatureEnabled: false, -}; - -export const stateKey = 'featureFlags'; - -export function featureFlagsReducer(state = defaultState, action: FeatureFlagsAction): FeatureFlagsState { - switch (action.type) { - case getType(setFeatureFlags): - return { - ...state, - hasFeatureFlags: true, - isClusterInfoFeatureEnabled: action.payload.isClusterInfoFeatureEnabled, - isExportsFeatureEnabled: action.payload.isExportsFeatureEnabled, - isFinsightsFeatureEnabled: action.payload.isFinsightsFeatureEnabled, - isIbmFeatureEnabled: action.payload.isIbmFeatureEnabled, - isRosFeatureEnabled: action.payload.isRosFeatureEnabled, - isSettingsPlatformFeatureEnabled: action.payload.isSettingsPlatformFeatureEnabled, - isTagMappingFeatureEnabled: action.payload.isTagMappingFeatureEnabled, - }; - - default: - return state; - } -} diff --git a/src/store/featureFlags/featureFlagsSelectors.ts b/src/store/featureFlags/featureFlagsSelectors.ts deleted file mode 100644 index 507f0e4dc..000000000 --- a/src/store/featureFlags/featureFlagsSelectors.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { RootState } from 'store/rootReducer'; - -import { stateKey } from './featureFlagsReducer'; - -export const selectFeatureFlagsState = (state: RootState) => state[stateKey]; - -export const selectHasFeatureFlags = (state: RootState) => selectFeatureFlagsState(state).hasFeatureFlags; - -export const selectIsClusterInfoFeatureEnabled = (state: RootState) => - selectFeatureFlagsState(state).isClusterInfoFeatureEnabled; -export const selectIsExportsFeatureEnabled = (state: RootState) => - selectFeatureFlagsState(state).isExportsFeatureEnabled; -export const selectIsFinsightsFeatureEnabled = (state: RootState) => - selectFeatureFlagsState(state).isFinsightsFeatureEnabled; -export const selectIsIbmFeatureEnabled = (state: RootState) => selectFeatureFlagsState(state).isIbmFeatureEnabled; -export const selectIsRosFeatureEnabled = (state: RootState) => selectFeatureFlagsState(state).isRosFeatureEnabled; -export const selectIsSettingsPlatformFeatureEnabled = (state: RootState) => - selectFeatureFlagsState(state).isSettingsPlatformFeatureEnabled; -export const selectIsTagMappingFeatureEnabled = (state: RootState) => - selectFeatureFlagsState(state).isTagMappingFeatureEnabled; diff --git a/src/store/featureFlags/index.ts b/src/store/featureFlags/index.ts deleted file mode 100644 index 844920fad..000000000 --- a/src/store/featureFlags/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as featureFlagsActions from './featureFlagsActions'; -import type { FeatureFlagsAction, FeatureFlagsState } from './featureFlagsReducer'; -import { featureFlagsReducer, stateKey as featureFlagsStateKey } from './featureFlagsReducer'; -import * as featureFlagsSelectors from './featureFlagsSelectors'; - -export type { FeatureFlagsAction, FeatureFlagsState }; -export { featureFlagsActions, featureFlagsReducer, featureFlagsSelectors, featureFlagsStateKey }; diff --git a/src/store/featureToggle/__snapshots__/featureToggle.test.ts.snap b/src/store/featureToggle/__snapshots__/featureToggle.test.ts.snap new file mode 100644 index 000000000..38f05a822 --- /dev/null +++ b/src/store/featureToggle/__snapshots__/featureToggle.test.ts.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`default state 1`] = ` +{ + "hasFeatureToggle": false, + "isClusterInfoToggleEnabled": false, + "isDebugToggleEnabled": false, + "isExportsToggleEnabled": false, + "isFinsightsToggleEnabled": false, + "isIbmToggleEnabled": false, + "isRosToggleEnabled": false, + "isSettingsPlatformToggleEnabled": false, + "isTagMappingToggleEnabled": false, +} +`; diff --git a/src/store/featureToggle/featureToggle.test.ts b/src/store/featureToggle/featureToggle.test.ts new file mode 100644 index 000000000..f5bab857c --- /dev/null +++ b/src/store/featureToggle/featureToggle.test.ts @@ -0,0 +1,39 @@ +import { createMockStoreCreator } from 'store/mockStore'; + +import { FeatureToggleSelectors } from '.'; +import * as actions from './featureToggleActions'; +import { FeatureToggleReducer, stateKey } from './featureToggleReducer'; +import * as selectors from './featureToggleSelectors'; + +const createUIStore = createMockStoreCreator({ + [stateKey]: FeatureToggleReducer, +}); + +test('default state', async () => { + const store = createUIStore(); + expect(selectors.selectFeatureToggleState(store.getState())).toMatchSnapshot(); +}); + +test('Exports feature is enabled', async () => { + const store = createUIStore(); + store.dispatch(actions.setFeatureToggle({ isExportsToggleEnabled: true })); + expect(FeatureToggleSelectors.selectIsExportsToggleEnabled(store.getState())).toBe(true); +}); + +test('FINsights feature is enabled', async () => { + const store = createUIStore(); + store.dispatch(actions.setFeatureToggle({ isFinsightsToggleEnabled: true })); + expect(FeatureToggleSelectors.selectIsFinsightsToggleEnabled(store.getState())).toBe(true); +}); + +test('IBM feature is enabled', async () => { + const store = createUIStore(); + store.dispatch(actions.setFeatureToggle({ isIbmToggleEnabled: true })); + expect(FeatureToggleSelectors.selectIsIbmToggleEnabled(store.getState())).toBe(true); +}); + +test('ROS feature is enabled', async () => { + const store = createUIStore(); + store.dispatch(actions.setFeatureToggle({ isRosToggleEnabled: true })); + expect(FeatureToggleSelectors.selectIsRosToggleEnabled(store.getState())).toBe(true); +}); diff --git a/src/store/featureToggle/featureToggleActions.ts b/src/store/featureToggle/featureToggleActions.ts new file mode 100644 index 000000000..6a7df2a8c --- /dev/null +++ b/src/store/featureToggle/featureToggleActions.ts @@ -0,0 +1,15 @@ +import { createAction } from 'typesafe-actions'; + +export interface FeatureToggleActionMeta { + isClusterInfoToggleEnabled?: boolean; + isDebugToggleEnabled?: boolean; + isExportsToggleEnabled?: boolean; + isFinsightsToggleEnabled?: boolean; + isIbmToggleEnabled?: boolean; + isRosToggleEnabled?: boolean; + isSettingsPlatformToggleEnabled?: boolean; + isTagMappingToggleEnabled?: boolean; +} + +export const setFeatureToggle = createAction('feature/init_feature_toggle')(); +export const resetState = createAction('feature/reset_state')(); diff --git a/src/store/featureToggle/featureToggleReducer.ts b/src/store/featureToggle/featureToggleReducer.ts new file mode 100644 index 000000000..b2927f9a7 --- /dev/null +++ b/src/store/featureToggle/featureToggleReducer.ts @@ -0,0 +1,54 @@ +import type { ActionType } from 'typesafe-actions'; +import { getType } from 'typesafe-actions'; + +import type { resetState } from './featureToggleActions'; +import { setFeatureToggle } from './featureToggleActions'; + +export type FeatureToggleAction = ActionType; + +export type FeatureToggleState = Readonly<{ + hasFeatureToggle: boolean; + isClusterInfoToggleEnabled: boolean; + isDebugToggleEnabled: boolean; + isExportsToggleEnabled: boolean; + isFinsightsToggleEnabled: boolean; + isIbmToggleEnabled: boolean; + isRosToggleEnabled: boolean; + isSettingsPlatformToggleEnabled: boolean; + isTagMappingToggleEnabled: boolean; +}>; + +export const defaultState: FeatureToggleState = { + hasFeatureToggle: false, + isClusterInfoToggleEnabled: false, + isDebugToggleEnabled: false, + isExportsToggleEnabled: false, + isFinsightsToggleEnabled: false, + isIbmToggleEnabled: false, + isRosToggleEnabled: false, + isSettingsPlatformToggleEnabled: false, + isTagMappingToggleEnabled: false, +}; + +export const stateKey = 'FeatureToggle'; + +export function FeatureToggleReducer(state = defaultState, action: FeatureToggleAction): FeatureToggleState { + switch (action.type) { + case getType(setFeatureToggle): + return { + ...state, + hasFeatureToggle: true, + isClusterInfoToggleEnabled: action.payload.isClusterInfoToggleEnabled, + isDebugToggleEnabled: action.payload.isDebugToggleEnabled, + isExportsToggleEnabled: action.payload.isExportsToggleEnabled, + isFinsightsToggleEnabled: action.payload.isFinsightsToggleEnabled, + isIbmToggleEnabled: action.payload.isIbmToggleEnabled, + isRosToggleEnabled: action.payload.isRosToggleEnabled, + isSettingsPlatformToggleEnabled: action.payload.isSettingsPlatformToggleEnabled, + isTagMappingToggleEnabled: action.payload.isTagMappingToggleEnabled, + }; + + default: + return state; + } +} diff --git a/src/store/featureToggle/featureToggleSelectors.ts b/src/store/featureToggle/featureToggleSelectors.ts new file mode 100644 index 000000000..a5d243350 --- /dev/null +++ b/src/store/featureToggle/featureToggleSelectors.ts @@ -0,0 +1,21 @@ +import type { RootState } from 'store/rootReducer'; + +import { stateKey } from './featureToggleReducer'; + +export const selectFeatureToggleState = (state: RootState) => state[stateKey]; + +export const selectHasFeatureToggle = (state: RootState) => selectFeatureToggleState(state).hasFeatureToggle; + +export const selectIsClusterInfoToggleEnabled = (state: RootState) => + selectFeatureToggleState(state).isClusterInfoToggleEnabled; +export const selectIsDebugToggleEnabled = (state: RootState) => selectFeatureToggleState(state).isDebugToggleEnabled; +export const selectIsExportsToggleEnabled = (state: RootState) => + selectFeatureToggleState(state).isExportsToggleEnabled; +export const selectIsFinsightsToggleEnabled = (state: RootState) => + selectFeatureToggleState(state).isFinsightsToggleEnabled; +export const selectIsIbmToggleEnabled = (state: RootState) => selectFeatureToggleState(state).isIbmToggleEnabled; +export const selectIsRosToggleEnabled = (state: RootState) => selectFeatureToggleState(state).isRosToggleEnabled; +export const selectIsSettingsPlatformToggleEnabled = (state: RootState) => + selectFeatureToggleState(state).isSettingsPlatformToggleEnabled; +export const selectIsTagMappingToggleEnabled = (state: RootState) => + selectFeatureToggleState(state).isTagMappingToggleEnabled; diff --git a/src/store/featureToggle/index.ts b/src/store/featureToggle/index.ts new file mode 100644 index 000000000..f5525ee90 --- /dev/null +++ b/src/store/featureToggle/index.ts @@ -0,0 +1,7 @@ +import * as FeatureToggleActions from './featureToggleActions'; +import type { FeatureToggleAction, FeatureToggleState } from './featureToggleReducer'; +import { FeatureToggleReducer, stateKey as FeatureToggleStateKey } from './featureToggleReducer'; +import * as FeatureToggleSelectors from './featureToggleSelectors'; + +export type { FeatureToggleAction, FeatureToggleState }; +export { FeatureToggleActions, FeatureToggleReducer, FeatureToggleSelectors, FeatureToggleStateKey }; diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts index 765204aba..299b9991e 100644 --- a/src/store/rootReducer.ts +++ b/src/store/rootReducer.ts @@ -44,7 +44,7 @@ import { sourcesReducer, sourcesStateKey } from 'store/sourceSettings'; import { tagReducer, tagStateKey } from 'store/tags'; import type { StateType } from 'typesafe-actions'; -import { featureFlagsReducer, featureFlagsStateKey } from './featureFlags'; +import { FeatureToggleReducer, FeatureToggleStateKey } from './featureToggle'; import { metricsReducer, metricsStateKey } from './metrics'; import { providersReducer, providersStateKey } from './providers'; import { rbacReducer, rbacStateKey } from './rbac'; @@ -68,7 +68,7 @@ export const rootReducer = combineReducers({ [ociHistoricalDataStateKey]: ociHistoricalDataReducer, [costModelsStateKey]: costModelsReducer, [exportStateKey]: exportReducer, - [featureFlagsStateKey]: featureFlagsReducer, + [FeatureToggleStateKey]: FeatureToggleReducer, [forecastStateKey]: forecastReducer, [gcpCostOverviewStateKey]: gcpCostOverviewReducer, [gcpDashboardStateKey]: gcpDashboardReducer, From bd1682de6a1126628b049d8f067a96ee107da7eb Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Wed, 6 Mar 2024 12:22:16 -0500 Subject: [PATCH 22/61] Dependency updates --- package-lock.json | 30 ++++++------------- package.json | 7 ++--- .../components/charts/costChart/costChart.tsx | 2 +- .../costExplorerChart/costExplorerChart.tsx | 2 +- .../charts/dailyCostChart/dailyCostChart.tsx | 2 +- .../dailyTrendChart/dailyTrendChart.tsx | 2 +- .../historicalCostChart.tsx | 2 +- .../historicalTrendChart.tsx | 2 +- .../historicalUsageChart.tsx | 2 +- .../charts/trendChart/trendChart.tsx | 2 +- .../charts/usageChart/usageChart.tsx | 2 +- 11 files changed, 21 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75e090db7..9cf87dbab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "GNU AGPLv3", "dependencies": { "@patternfly/patternfly": "^5.2.0", - "@patternfly/react-charts": "^7.2.1", + "@patternfly/react-charts": "^7.2.2", "@patternfly/react-component-groups": "^5.0.0", "@patternfly/react-core": "^5.2.0", "@patternfly/react-icons": "^5.2.0", @@ -38,8 +38,7 @@ "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", "unleash-proxy-client": "^3.3.1", - "victory-core": "^36.9.1", - "yaml": "^2.4.0" + "victory-core": "^36.9.1" }, "devDependencies": { "@formatjs/cli": "^6.2.7", @@ -56,7 +55,7 @@ "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", "@types/react": "^18.2.63", - "@types/react-dom": "^18.2.19", + "@types/react-dom": "^18.2.20", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.1.1", @@ -2216,9 +2215,9 @@ "integrity": "sha512-phdsXcCRO+JICFXIKtORxSbOWoBr9zRCgtFTKTJ8hAIzm6wEUCdcHZrvsd+SXNR3q/4b/+KlmHUC4Q4KGUiuYw==" }, "node_modules/@patternfly/react-charts": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@patternfly/react-charts/-/react-charts-7.2.1.tgz", - "integrity": "sha512-ln24zv08JF7tFWY/SXlV5vKInqBU/2o4DuPg5bVhy/V6X3BjPY1aUn/Yrwqw/9H0NTMZPdIpqVt8066zWHAu8g==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@patternfly/react-charts/-/react-charts-7.2.2.tgz", + "integrity": "sha512-1PFuvXz3mm/o/O+BQ2/2e66ncvtV8XIYxFaimurslCLTygodOvjBDDu/D/5tNa3HLxvA+fm2Q58893POGZi+bw==", "dependencies": { "@patternfly/react-styles": "^5.2.1", "@patternfly/react-tokens": "^5.2.1", @@ -4076,9 +4075,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.19", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz", - "integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==", + "version": "18.2.20", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.20.tgz", + "integrity": "sha512-HXN/biJY8nv20Cn9ZbCFq3liERd4CozVZmKbaiZ9KiKTrWqsP7eoGDO6OOGvJQwoVFuiXaiJ7nBBjiFFbRmQMQ==", "dev": true, "dependencies": { "@types/react": "*" @@ -21923,17 +21922,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/yaml": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz", - "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 0067420bd..c314fff2f 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ }, "dependencies": { "@patternfly/patternfly": "^5.2.0", - "@patternfly/react-charts": "^7.2.1", + "@patternfly/react-charts": "^7.2.2", "@patternfly/react-component-groups": "^5.0.0", "@patternfly/react-core": "^5.2.0", "@patternfly/react-icons": "^5.2.0", @@ -78,8 +78,7 @@ "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", "unleash-proxy-client": "^3.3.1", - "victory-core": "^36.9.1", - "yaml": "^2.4.0" + "victory-core": "^36.9.1" }, "devDependencies": { "@formatjs/cli": "^6.2.7", @@ -96,7 +95,7 @@ "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", "@types/react": "^18.2.63", - "@types/react-dom": "^18.2.19", + "@types/react-dom": "^18.2.20", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.1.1", diff --git a/src/routes/components/charts/costChart/costChart.tsx b/src/routes/components/charts/costChart/costChart.tsx index b5f3fa232..672697766 100644 --- a/src/routes/components/charts/costChart/costChart.tsx +++ b/src/routes/components/charts/costChart/costChart.tsx @@ -327,7 +327,7 @@ class CostChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} + title={datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} /> ), } as any) diff --git a/src/routes/components/charts/costExplorerChart/costExplorerChart.tsx b/src/routes/components/charts/costExplorerChart/costExplorerChart.tsx index 4760d2fbe..8d1a161e4 100644 --- a/src/routes/components/charts/costExplorerChart/costExplorerChart.tsx +++ b/src/routes/components/charts/costExplorerChart/costExplorerChart.tsx @@ -443,7 +443,7 @@ class CostExplorerChartBase extends React.Component intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} + title={datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} /> ), } as any) diff --git a/src/routes/components/charts/dailyCostChart/dailyCostChart.tsx b/src/routes/components/charts/dailyCostChart/dailyCostChart.tsx index b07e2eece..7ae5bcf50 100644 --- a/src/routes/components/charts/dailyCostChart/dailyCostChart.tsx +++ b/src/routes/components/charts/dailyCostChart/dailyCostChart.tsx @@ -378,7 +378,7 @@ class DailyCostChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} + title={datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} /> ), } as any) diff --git a/src/routes/components/charts/dailyTrendChart/dailyTrendChart.tsx b/src/routes/components/charts/dailyTrendChart/dailyTrendChart.tsx index 7dfdb4b49..9bd4ef8b2 100644 --- a/src/routes/components/charts/dailyTrendChart/dailyTrendChart.tsx +++ b/src/routes/components/charts/dailyTrendChart/dailyTrendChart.tsx @@ -400,7 +400,7 @@ class DailyTrendChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} + title={datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} /> ), } as any) diff --git a/src/routes/components/charts/historicalCostChart/historicalCostChart.tsx b/src/routes/components/charts/historicalCostChart/historicalCostChart.tsx index 77a33bf9d..da0c4ed31 100644 --- a/src/routes/components/charts/historicalCostChart/historicalCostChart.tsx +++ b/src/routes/components/charts/historicalCostChart/historicalCostChart.tsx @@ -270,7 +270,7 @@ class HistoricalCostChartBase extends React.Component intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} + title={datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} /> ), } as any) diff --git a/src/routes/components/charts/historicalTrendChart/historicalTrendChart.tsx b/src/routes/components/charts/historicalTrendChart/historicalTrendChart.tsx index 9ecff1a4f..6d288a19d 100644 --- a/src/routes/components/charts/historicalTrendChart/historicalTrendChart.tsx +++ b/src/routes/components/charts/historicalTrendChart/historicalTrendChart.tsx @@ -260,7 +260,7 @@ class HistoricalTrendChartBase extends React.Component intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} + title={datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} /> ), } as any) diff --git a/src/routes/components/charts/historicalUsageChart/historicalUsageChart.tsx b/src/routes/components/charts/historicalUsageChart/historicalUsageChart.tsx index 52c50fa9f..833ccd18f 100644 --- a/src/routes/components/charts/historicalUsageChart/historicalUsageChart.tsx +++ b/src/routes/components/charts/historicalUsageChart/historicalUsageChart.tsx @@ -370,7 +370,7 @@ class HistoricalUsageChartBase extends React.Component intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} + title={datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} /> ), } as any) diff --git a/src/routes/components/charts/trendChart/trendChart.tsx b/src/routes/components/charts/trendChart/trendChart.tsx index 12ff15a51..8f6a68a42 100644 --- a/src/routes/components/charts/trendChart/trendChart.tsx +++ b/src/routes/components/charts/trendChart/trendChart.tsx @@ -348,7 +348,7 @@ class TrendChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} + title={datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} /> ), } as any) diff --git a/src/routes/components/charts/usageChart/usageChart.tsx b/src/routes/components/charts/usageChart/usageChart.tsx index ff0310716..0c34b6df4 100644 --- a/src/routes/components/charts/usageChart/usageChart.tsx +++ b/src/routes/components/charts/usageChart/usageChart.tsx @@ -299,7 +299,7 @@ class UsageChartBase extends React.Component { labelComponent: ( intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })) as any} + title={datum => intl.formatMessage(messages.chartDayOfTheMonth, { day: datum.x })} /> ), } as any) From 9fa169a9929216a1bd5be8f8a788113dd71b478c Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Wed, 6 Mar 2024 19:11:03 -0500 Subject: [PATCH 23/61] Add initial accordion for tag mappings https://issues.redhat.com/browse/COST-3824 --- locales/data.json | 48 +++ locales/translations.json | 6 + src/locales/messages.ts | 34 ++ src/routes/components/dataTable/dataTable.tsx | 2 +- .../state/loadingState/loadingState.tsx | 1 - .../details/awsDetails/detailsTable.tsx | 1 + .../details/azureDetails/detailsTable.tsx | 1 + .../components/pvcChart/modal/pvcTable.tsx | 1 - .../details/gcpDetails/detailsTable.tsx | 1 + .../details/ibmDetails/detailsTable.tsx | 1 + .../details/ociDetails/detailsTable.tsx | 1 + .../details/ocpDetails/detailsTable.tsx | 1 + .../details/rhelDetails/detailsTable.tsx | 1 + .../costCategory/costCategoryTable.tsx | 1 + .../platformProjectsTable.tsx | 1 + .../settings/tagLabels/tagLabels.styles.ts | 18 +- src/routes/settings/tagLabels/tagLabels.tsx | 334 ++++-------------- .../settings/tagLabels/tagMappings/index.ts | 1 + .../tagMappings/tagMappings.styles.ts | 14 + .../tagLabels/tagMappings/tagMappings.tsx | 229 ++++++++++++ .../tagMappings/tagMappingsTable.tsx | 129 +++++++ .../tagMappings/tagMappingsToolbar.tsx | 186 ++++++++++ src/routes/settings/tagLabels/tags/index.ts | 1 + .../settings/tagLabels/tags/tags.styles.ts | 14 + src/routes/settings/tagLabels/tags/tags.tsx | 295 ++++++++++++++++ .../{tagTable.tsx => tags/tagsTable.tsx} | 17 +- .../{tagToolbar.tsx => tags/tagsToolbar.tsx} | 30 +- 27 files changed, 1071 insertions(+), 298 deletions(-) create mode 100644 src/routes/settings/tagLabels/tagMappings/index.ts create mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts create mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappings.tsx create mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx create mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx create mode 100644 src/routes/settings/tagLabels/tags/index.ts create mode 100644 src/routes/settings/tagLabels/tags/tags.styles.ts create mode 100644 src/routes/settings/tagLabels/tags/tags.tsx rename src/routes/settings/tagLabels/{tagTable.tsx => tags/tagsTable.tsx} (89%) rename src/routes/settings/tagLabels/{tagToolbar.tsx => tags/tagsToolbar.tsx} (87%) diff --git a/locales/data.json b/locales/data.json index 64e3cbb18..80fabf3f3 100644 --- a/locales/data.json +++ b/locales/data.json @@ -1941,6 +1941,12 @@ "value": "Create rate" } ], + "createTagMapping": [ + { + "type": 0, + "value": "Create tag mapping" + } + ], "currency": [ { "type": 0, @@ -3530,6 +3536,12 @@ "value": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-using-cost-models#understanding-cost-distribution_using-cost-models" } ], + "docsTagMappings": [ + { + "type": 0, + "value": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/index" + } + ], "docsTags": [ { "type": 0, @@ -11890,6 +11902,42 @@ "value": "Tags and labels" } ], + "tagLabelsEnable": [ + { + "type": 0, + "value": "Enable tags and labels" + } + ], + "tagLabelsMap": [ + { + "type": 0, + "value": "Map tags and labels" + } + ], + "tagMappingDesc": [ + { + "type": 0, + "value": "Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. " + }, + { + "type": 1, + "value": "warning" + }, + { + "type": 0, + "value": " Changes will be reflected within 24 hours. " + }, + { + "type": 1, + "value": "learnMore" + } + ], + "tagMappingWarning": [ + { + "type": 0, + "value": "You must enable tags to use tag mapping." + } + ], "tagNames": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 947b8c971..d3f1cd710 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -183,6 +183,7 @@ "createCostModelNoContinue": "No, I want to continue", "createCostModelTitle": "Create a cost model", "createRate": "Create rate", + "createTagMapping": "Create tag mapping", "currency": "Currency", "currencyAbbreviations": "{symbol, select, billion {{value} B} million {{value} M} quadrillion {{value} q} thousand {{value} K} trillion {{value} t} other {}}", "currencyCalcuationsTitle": "Currency and calculations", @@ -240,6 +241,7 @@ "docsCostModelsMarkup": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-setting-up-cost-models#creating-an-AWS-Azure-cost-model_setting-up-cost-models", "docsCostModelsOcp": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-setting-up-cost-models#creating-an-ocp-cost-model_setting-up-cost-models", "docsPlatformProjects": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-using-cost-models#understanding-cost-distribution_using-cost-models", + "docsTagMappings": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/index", "docsTags": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/assembly-configuring-tags-and-labels-in-cost-management", "docsUsingCostModels": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html-single/using_cost_models/index", "download": "Download", @@ -536,6 +538,10 @@ "tagHeadingTitle": "Tags ({value})", "tagHeadingValue": "Value", "tagLabels": "Tags and labels", + "tagLabelsEnable": "Enable tags and labels", + "tagLabelsMap": "Map tags and labels", + "tagMappingDesc": "Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}", + "tagMappingWarning": "You must enable tags to use tag mapping.", "tagNames": "Tag names", "timeOfExport": "Time of export", "to": "to", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index cb520bebd..aa6ee0567 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -1003,6 +1003,11 @@ export default defineMessages({ description: 'Create rate', id: 'createRate', }, + createTagMapping: { + defaultMessage: 'Create tag mapping', + description: 'Create tag mapping', + id: 'createTagMapping', + }, currency: { defaultMessage: 'Currency', description: 'Currency', @@ -1430,6 +1435,13 @@ export default defineMessages({ 'https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-using-cost-models#understanding-cost-distribution_using-cost-models', id: 'docsPlatformProjects', }, + docsTagMappings: { + defaultMessage: + 'https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/index', + description: + 'https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/index', + id: 'docsTagMappings', + }, docsTags: { defaultMessage: 'https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/assembly-configuring-tags-and-labels-in-cost-management', @@ -3295,11 +3307,33 @@ export default defineMessages({ description: 'Value', id: 'tagHeadingValue', }, + tagLabelsEnable: { + defaultMessage: 'Enable tags and labels', + description: 'Enable tags and labels', + id: 'tagLabelsEnable', + }, + tagLabelsMap: { + defaultMessage: 'Map tags and labels', + description: 'Map tags and labels', + id: 'tagLabelsMap', + }, tagLabelsTitle: { defaultMessage: 'Tags and labels', description: 'Tags and labels', id: 'tagLabels', }, + tagMappingDesc: { + defaultMessage: + 'Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}', + description: + 'Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}', + id: 'tagMappingDesc', + }, + tagMappingWarning: { + defaultMessage: 'You must enable tags to use tag mapping.', + description: 'You must enable tags to use tag mapping.', + id: 'tagMappingWarning', + }, tagNames: { defaultMessage: 'Tag names', description: 'Tag Names', diff --git a/src/routes/components/dataTable/dataTable.tsx b/src/routes/components/dataTable/dataTable.tsx index 454ea773e..09d11a9fb 100644 --- a/src/routes/components/dataTable/dataTable.tsx +++ b/src/routes/components/dataTable/dataTable.tsx @@ -119,7 +119,7 @@ class DataTable extends React.Component { }; public render() { - const { columns, intl, isActionsCell = false, isLoading, isSelectable = true, rows } = this.props; + const { columns, intl, isActionsCell = false, isLoading, isSelectable, rows } = this.props; return ( <> diff --git a/src/routes/components/state/loadingState/loadingState.tsx b/src/routes/components/state/loadingState/loadingState.tsx index eed2a95d8..bb3af4d76 100644 --- a/src/routes/components/state/loadingState/loadingState.tsx +++ b/src/routes/components/state/loadingState/loadingState.tsx @@ -14,7 +14,6 @@ interface LoadingStateProps extends WrappedComponentProps { // defaultIntl required for testing const LoadingStateBase: React.FC = ({ intl = defaultIntl, - body = intl.formatMessage(messages.loadingStateDesc), heading = intl.formatMessage(messages.loadingStateTitle), }) => { diff --git a/src/routes/details/awsDetails/detailsTable.tsx b/src/routes/details/awsDetails/detailsTable.tsx index 792257e58..d4b81acdd 100644 --- a/src/routes/details/awsDetails/detailsTable.tsx +++ b/src/routes/details/awsDetails/detailsTable.tsx @@ -299,6 +299,7 @@ class DetailsTableBase extends React.Component = ({ filterBy, isLoading, onSort, orderB columns={columns} filterBy={filterBy} isLoading={isLoading} - isSelectable={false} onSort={handleOnSort} orderBy={orderBy} rows={rows} diff --git a/src/routes/details/gcpDetails/detailsTable.tsx b/src/routes/details/gcpDetails/detailsTable.tsx index 65e643de2..56e855f68 100644 --- a/src/routes/details/gcpDetails/detailsTable.tsx +++ b/src/routes/details/gcpDetails/detailsTable.tsx @@ -271,6 +271,7 @@ class DetailsTableBase extends React.Component { + switch (item) { + case TagLabelsItem.tagMappings: + return 'tagMappings'; + case TagLabelsItem.tags: + return 'tags'; + } +}; + +interface AvailableItem { + item: TagLabelsItem; + label: string; } -export interface TagLabelsMapProps { - query?: Query; +interface TagLabelsOwnProps { + canWrite?: boolean; } export interface TagLabelsStateProps { isTagMappingToggleEnabled?: boolean; - settings?: Settings; - settingsError?: AxiosError; - settingsStatus?: FetchStatus; - settingsQueryString?: string; } type TagLabelsProps = TagLabelsOwnProps; -const baseQuery: Query = { - limit: 10, - offset: 0, - filter_by: {}, - order_by: { - key: 'asc', - }, -}; - const TagLabels: React.FC = ({ canWrite }) => { - const [query, setQuery] = useState({ ...baseQuery }); - const [selectedItems, setSelectedItems] = useStateCallback([]); - const dispatch: ThunkDispatch = useDispatch(); + const [activeKey, setActiveKey] = useState(0); + const isTagMappingToggleEnabled = useIsTagMappingToggleEnabled(); const intl = useIntl(); - const { isTagMappingToggleEnabled, settings, settingsError, settingsStatus } = useMapToProps({ query }); - - const getTags = () => { - if (settings) { - return settings.data as any; - } - return []; - }; - - const getPagination = (isDisabled = false, isBottom = false) => { - const count = settings?.meta ? settings.meta.count : 0; - const limit = settings?.meta ? settings.meta.limit : baseQuery.limit; - const offset = settings?.meta ? settings.meta.offset : baseQuery.offset; - const page = Math.trunc(offset / limit + 1); - - return ( - handleOnPerPageSelect(perPage)} - onSetPage={(event, pageNumber) => handleOnSetPage(pageNumber)} - page={page} - perPage={limit} - titles={{ - paginationAriaLabel: intl.formatMessage(messages.paginationTitle, { - title: intl.formatMessage(messages.openShift), - placement: isBottom ? 'bottom' : 'top', - }), - }} - variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} - /> - ); - }; - - const getTable = () => { - return ( - handleOnSort(sortType, isSortAscending)} - settings={settings} - selectedItems={selectedItems} - /> - ); + const getAvailableItems = () => { + const availableItems: AvailableItem[] = [ + { + item: TagLabelsItem.tags, + label: intl.formatMessage(messages.tagLabelsEnable), + }, + { + item: TagLabelsItem.tagMappings, + label: intl.formatMessage(messages.tagLabelsMap), + }, + ]; + return availableItems; }; - const getToolbar = (tags: SettingsData[]) => { - const hasEnabledItem = selectedItems.find(item => item.enabled); - const hasDisabledItem = selectedItems.find(item => !item.enabled); - const itemsTotal = settings?.meta ? settings.meta.count : 0; - const enabledTagsCount = settings?.meta ? settings.meta.enabled_tags_count : 0; - const enabledTagsLimit = settings?.meta ? settings.meta.enabled_tags_limit : 0; - - return ( - handleOnFilterAdded(filter)} - onFilterRemoved={filter => handleOnFilterRemoved(filter)} - pagination={getPagination(isDisabled)} - query={query} - selectedItems={selectedItems} - showBulkSelectAll={false} - /> - ); - }; - - const handleOnBulkSelect = (action: string) => { - if (action === 'none') { - setSelectedItems([]); - } else if (action === 'page') { - const newSelectedItems = [...selectedItems]; - getTags().map(val => { - if (!newSelectedItems.find(item => item.uuid === val.uuid)) { - newSelectedItems.push(val); - } - }); - setSelectedItems(newSelectedItems); + const handleOnClick = (event, index) => { + if (activeKey !== index) { + setActiveKey(index); } }; - const handleOnDisableTags = () => { - if (selectedItems.length > 0) { - setSelectedItems([], () => { - dispatch( - settingsActions.updateSettings(SettingsType.tagsDisable, { - ids: selectedItems.map(item => item.uuid), - }) - ); - }); - } - }; + const getAccordionContent = (item: TagLabelsItem, index: number) => { + const emptyTab = <>; // Lazily load tabs - const handleOnEnableTags = () => { - if (selectedItems.length > 0) { - setSelectedItems([], () => { - dispatch( - settingsActions.updateSettings(SettingsType.tagsEnable, { - ids: selectedItems.map(item => item.uuid), - }) - ); - }); + if (activeKey !== index) { + return emptyTab; } - }; - const handleOnFilterAdded = filter => { - const newQuery = queryUtils.handleOnFilterAdded(query, filter); - setQuery(newQuery); - }; - - const handleOnFilterRemoved = filter => { - const newQuery = queryUtils.handleOnFilterRemoved(query, filter); - setQuery(newQuery); - }; - - const handleOnPerPageSelect = perPage => { - const newQuery = queryUtils.handleOnPerPageSelect(query, perPage, true); - setQuery(newQuery); - }; - - const handleOnSetPage = pageNumber => { - const newQuery = queryUtils.handleOnSetPage(query, settings, pageNumber, true); - setQuery(newQuery); - }; - - const handleonSelect = (items: SettingsData[], isSelected: boolean = false) => { - let newItems = [...selectedItems]; - if (items && items.length > 0) { - if (isSelected) { - items.map(item => newItems.push(item)); - } else { - items.map(item => { - newItems = newItems.filter(val => val.uuid !== item.uuid); - }); - } + const currentItem = getIdKeyForItem(item); + if (currentItem === TagLabelsItem.tagMappings) { + return ; + } else if (currentItem === TagLabelsItem.tags) { + return ; + } else { + return emptyTab; } - setSelectedItems(newItems); }; - const handleOnSort = (sortType, isSortAscending) => { - const newQuery = queryUtils.handleOnSort(query, sortType, isSortAscending); - setQuery(newQuery); + const getAccordionItem = (availableItems: AvailableItem[]) => { + return availableItems.map((val, index) => { + return ( + + { + handleOnClick(_evt, index); + }} + > + {val.label} + + {getAccordionContent(val.item, index)} + + ); + }); }; - if (settingsError) { - return ; - } - - const tags = getTags(); - const isDisabled = tags.length === 0; - const enabledTagsLimit = settings?.meta ? settings.meta.enabled_tags_limit : 0; + const availableItems = getAvailableItems(); return ( -
- {intl.formatMessage(messages.tagDesc, { - count: enabledTagsLimit, - learnMore: ( - - {intl.formatMessage(messages.learnMore)} - - ), - })} +
+ {isTagMappingToggleEnabled ? ( + {getAccordionItem(availableItems)} + ) : ( + + )}
- {getToolbar(tags)} - {settingsStatus === FetchStatus.inProgress ? ( - - ) : ( - <> - {getTable()} -
{getPagination(isDisabled, true)}
- {isTagMappingToggleEnabled && TEST} - - )} ); }; -// eslint-disable-next-line no-empty-pattern -const useMapToProps = ({ query }: TagLabelsMapProps): TagLabelsStateProps => { - const dispatch: ThunkDispatch = useDispatch(); - - const settingsQuery = { - filter_by: query.filter_by, - limit: query.limit, - offset: query.offset, - order_by: query.order_by, - }; - const settingsQueryString = getQuery(settingsQuery); - const settings = useSelector((state: RootState) => - settingsSelectors.selectSettings(state, SettingsType.tags, settingsQueryString) - ); - const settingsStatus = useSelector((state: RootState) => - settingsSelectors.selectSettingsStatus(state, SettingsType.tags, settingsQueryString) - ); - const settingsError = useSelector((state: RootState) => - settingsSelectors.selectSettingsError(state, SettingsType.tags, settingsQueryString) - ); - - const settingsUpdateDisableStatus = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsDisable) - ); - const settingsUpdateEnableStatus = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsEnable) - ); - - useEffect(() => { - if ( - !settingsError && - settingsStatus !== FetchStatus.inProgress && - settingsUpdateDisableStatus !== FetchStatus.inProgress && - settingsUpdateEnableStatus !== FetchStatus.inProgress - ) { - dispatch(settingsActions.fetchSettings(SettingsType.tags, settingsQueryString)); - } - }, [query, settingsUpdateDisableStatus, settingsUpdateEnableStatus]); - - return { - isTagMappingToggleEnabled: useIsTagMappingToggleEnabled(), - settings, - settingsError, - settingsStatus, - settingsQueryString, - }; -}; - export default TagLabels; diff --git a/src/routes/settings/tagLabels/tagMappings/index.ts b/src/routes/settings/tagLabels/tagMappings/index.ts new file mode 100644 index 000000000..e9d994c6d --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/index.ts @@ -0,0 +1 @@ +export { default as TagMappings } from './tagMappings'; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts b/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts new file mode 100644 index 000000000..690536658 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts @@ -0,0 +1,14 @@ +import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; +import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; +import type React from 'react'; + +export const styles = { + action: { + marginLeft: global_spacer_md.var, + }, + pagination: { + backgroundColor: global_BackgroundColor_light_100.value, + paddingBottom: global_spacer_md.value, + paddingTop: global_spacer_md.value, + }, +} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx new file mode 100644 index 000000000..e7fa8a531 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx @@ -0,0 +1,229 @@ +import { Pagination, PaginationVariant } from '@patternfly/react-core'; +import type { Query } from 'api/queries/query'; +import { getQuery } from 'api/queries/query'; +import type { Settings, SettingsData } from 'api/settings'; +import { SettingsType } from 'api/settings'; +import type { AxiosError } from 'axios'; +import messages from 'locales/messages'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import { NotAvailable } from 'routes/components/page/notAvailable'; +import { LoadingState } from 'routes/components/state/loadingState'; +import * as queryUtils from 'routes/utils/query'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { settingsActions, settingsSelectors } from 'store/settings'; + +import { styles } from './tagMappings.styles'; +import { TagMappingsTable } from './tagMappingsTable'; +import { TagMappingsToolbar } from './tagMappingsToolbar'; + +interface MappingsOwnProps { + canWrite?: boolean; +} + +export interface MappingsMapProps { + query?: Query; +} + +export interface MappingsStateProps { + settings?: Settings; + settingsError?: AxiosError; + settingsStatus?: FetchStatus; + settingsQueryString?: string; +} + +type MappingsProps = MappingsOwnProps; + +const baseQuery: Query = { + limit: 10, + offset: 0, + filter_by: {}, + order_by: { + key: 'asc', + }, +}; + +const TagMappings: React.FC = ({ canWrite }) => { + const [query, setQuery] = useState({ ...baseQuery }); + const intl = useIntl(); + + const { settings, settingsError, settingsStatus } = useMapToProps({ query }); + + const getMappings = () => { + if (settings) { + return settings.data as any; + } + return []; + }; + + const getPagination = (isDisabled = false, isBottom = false) => { + const count = settings?.meta ? settings.meta.count : 0; + const limit = settings?.meta ? settings.meta.limit : baseQuery.limit; + const offset = settings?.meta ? settings.meta.offset : baseQuery.offset; + const page = Math.trunc(offset / limit + 1); + + return ( + handleOnPerPageSelect(perPage)} + onSetPage={(event, pageNumber) => handleOnSetPage(pageNumber)} + page={page} + perPage={limit} + titles={{ + paginationAriaLabel: intl.formatMessage(messages.paginationTitle, { + title: intl.formatMessage(messages.openShift), + placement: isBottom ? 'bottom' : 'top', + }), + }} + variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} + widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + /> + ); + }; + + const getTable = () => { + return ( + handleOnSort(sortType, isSortAscending)} + settings={settings} + /> + ); + }; + + const getToolbar = (tags: SettingsData[]) => { + const itemsTotal = settings?.meta ? settings.meta.count : 0; + + return ( + handleOnFilterAdded(filter)} + onFilterRemoved={filter => handleOnFilterRemoved(filter)} + pagination={getPagination(isDisabled)} + query={query} + /> + ); + }; + + const handleOnCreateTagMapping = () => { + // Todo: Open wizard + }; + + const handleOnFilterAdded = filter => { + const newQuery = queryUtils.handleOnFilterAdded(query, filter); + setQuery(newQuery); + }; + + const handleOnFilterRemoved = filter => { + const newQuery = queryUtils.handleOnFilterRemoved(query, filter); + setQuery(newQuery); + }; + + const handleOnPerPageSelect = perPage => { + const newQuery = queryUtils.handleOnPerPageSelect(query, perPage, true); + setQuery(newQuery); + }; + + const handleOnSetPage = pageNumber => { + const newQuery = queryUtils.handleOnSetPage(query, settings, pageNumber, true); + setQuery(newQuery); + }; + + const handleOnSort = (sortType, isSortAscending) => { + const newQuery = queryUtils.handleOnSort(query, sortType, isSortAscending); + setQuery(newQuery); + }; + + if (settingsError) { + return ; + } + + const tags = getMappings(); + const isDisabled = tags.length === 0; + + return ( + <> +
+ {intl.formatMessage(messages.tagMappingDesc, { + learnMore: ( + + {intl.formatMessage(messages.learnMore)} + + ), + warning: {intl.formatMessage(messages.tagMappingWarning)}, + })} +
+ {getToolbar(tags)} + {settingsStatus === FetchStatus.inProgress ? ( + + ) : ( + <> + {getTable()} +
{getPagination(isDisabled, true)}
+ + )} + + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = ({ query }: MappingsMapProps): MappingsStateProps => { + const dispatch: ThunkDispatch = useDispatch(); + + const settingsQuery = { + filter_by: query.filter_by, + limit: query.limit, + offset: query.offset, + order_by: query.order_by, + }; + const settingsQueryString = getQuery(settingsQuery); + const settings = useSelector((state: RootState) => + settingsSelectors.selectSettings(state, SettingsType.tags, settingsQueryString) + ); + const settingsStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsStatus(state, SettingsType.tags, settingsQueryString) + ); + const settingsError = useSelector((state: RootState) => + settingsSelectors.selectSettingsError(state, SettingsType.tags, settingsQueryString) + ); + + const settingsUpdateDisableStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsDisable) + ); + const settingsUpdateEnableStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsEnable) + ); + + useEffect(() => { + if ( + !settingsError && + settingsStatus !== FetchStatus.inProgress && + settingsUpdateDisableStatus !== FetchStatus.inProgress && + settingsUpdateEnableStatus !== FetchStatus.inProgress + ) { + dispatch(settingsActions.fetchSettings(SettingsType.tags, settingsQueryString)); + } + }, [query, settingsUpdateDisableStatus, settingsUpdateEnableStatus]); + + return { + settings, + settingsError, + settingsStatus, + settingsQueryString, + }; +}; + +export default TagMappings; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx new file mode 100644 index 000000000..f66ac36f1 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx @@ -0,0 +1,129 @@ +import 'routes/components/dataTable/dataTable.scss'; + +import { Label } from '@patternfly/react-core'; +import type { Settings } from 'api/settings'; +import messages from 'locales/messages'; +import React from 'react'; +import type { WrappedComponentProps } from 'react-intl'; +import { injectIntl } from 'react-intl'; +import { DataTable } from 'routes/components/dataTable'; +import type { RouterComponentProps } from 'utils/router'; +import { withRouter } from 'utils/router'; + +interface TagMappingsTableOwnProps extends RouterComponentProps, WrappedComponentProps { + canWrite?: boolean; + filterBy?: any; + isAllSelected?: boolean; + isLoading?: boolean; + onSort(value: string, isSortAscending: boolean); + orderBy?: any; + settings: Settings; +} + +interface TagMappingsTableState { + columns?: any[]; + rows?: any[]; +} + +type TagMappingsTableProps = TagMappingsTableOwnProps; + +class TagMappingsTableBase extends React.Component { + public state: TagMappingsTableState = { + columns: [], + rows: [], + }; + + public componentDidMount() { + this.initDatum(); + } + + public componentDidUpdate(prevProps: TagMappingsTableProps) { + const { settings } = this.props; + const currentReport = settings?.data ? JSON.stringify(settings.data) : ''; + const previousReport = prevProps?.settings.data ? JSON.stringify(prevProps.settings.data) : ''; + + if (previousReport !== currentReport) { + this.initDatum(); + } + } + + private initDatum = () => { + const { intl, settings } = this.props; + if (!settings) { + return; + } + + const rows = []; + const tags = settings?.data ? (settings.data as any) : []; + + const columns = [ + { + orderBy: 'key', + name: intl.formatMessage(messages.detailsResourceNames, { value: 'name' }), + ...(tags.length && { isSortable: true }), + }, + { + orderBy: 'enabled', + name: intl.formatMessage(messages.detailsResourceNames, { value: 'status' }), + ...(tags.length && { isSortable: true }), + }, + { + orderBy: 'source_type', + name: intl.formatMessage(messages.sourceType), + ...(tags.length && { isSortable: true }), + }, + ]; + + tags.map(item => { + rows.push({ + cells: [ + { + value: item.key ? item.key : '', + }, + { + value: item.enabled ? ( + + ) : ( + + ), + }, + { + value: intl.formatMessage(messages.sourceTypes, { value: item?.source_type?.toLowerCase() }), + }, + ], + item, + }); + }); + + const filteredColumns = (columns as any[]).filter(column => !column.hidden); + const filteredRows = rows.map(({ ...row }) => { + row.cells = row.cells.filter(cell => !cell.hidden); + return row; + }); + + this.setState({ + columns: filteredColumns, + rows: filteredRows, + }); + }; + + public render() { + const { filterBy, isLoading, onSort, orderBy } = this.props; + const { columns, rows } = this.state; + + return ( + + ); + } +} + +const TagMappingsTable = injectIntl(withRouter(TagMappingsTableBase)); + +export { TagMappingsTable }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx new file mode 100644 index 000000000..3da2ee389 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx @@ -0,0 +1,186 @@ +import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; +import type { OcpQuery } from 'api/queries/ocpQuery'; +import { ResourcePathsType } from 'api/resources/resource'; +import messages from 'locales/messages'; +import React from 'react'; +import type { WrappedComponentProps } from 'react-intl'; +import { injectIntl } from 'react-intl'; +import { connect } from 'react-redux'; +import { BasicToolbar } from 'routes/components/dataToolbar'; +import type { ToolbarChipGroupExt } from 'routes/components/dataToolbar/utils/common'; +import type { Filter } from 'routes/utils/filter'; +import { createMapStateToProps } from 'store/common'; + +interface TagMappingsToolbarOwnProps { + canWrite?: boolean; + enabledTagsCount?: number; + enabledTagsLimit?: number; + isAllSelected?: boolean; + isDisabled?: boolean; + isPrimaryActionDisabled?: boolean; + isSecondaryActionDisabled?: boolean; + itemsPerPage?: number; + itemsTotal?: number; + onCreateTagMapping(); + onFilterAdded(filter: Filter); + onFilterRemoved(filter: Filter); + pagination?: React.ReactNode; + query?: OcpQuery; +} + +interface TagMappingsToolbarStateProps { + // TBD... +} + +interface TagMappingsToolbarDispatchProps { + // TBD... +} + +interface TagMappingsToolbarState { + categoryOptions?: ToolbarChipGroupExt[]; +} + +type TagMappingsToolbarProps = TagMappingsToolbarOwnProps & + TagMappingsToolbarStateProps & + TagMappingsToolbarDispatchProps & + WrappedComponentProps; + +export class TagMappingsToolbarBase extends React.Component { + protected defaultState: TagMappingsToolbarState = {}; + public state: TagMappingsToolbarState = { ...this.defaultState }; + + public componentDidMount() { + this.setState({ + categoryOptions: this.getCategoryOptions(), + }); + } + + private getActions = () => { + const { canWrite, intl, isDisabled, onCreateTagMapping } = this.props; + + const getTooltip = children => { + if (!canWrite) { + const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); + return {children}; + } + return children; + }; + + return getTooltip( + + ); + }; + + private getCategoryOptions = (): ToolbarChipGroupExt[] => { + const { intl } = this.props; + + const options = [ + { + ariaLabelKey: 'name', + placeholderKey: 'name', + key: 'key', + name: intl.formatMessage(messages.filterByValues, { value: 'name' }), + }, + { + key: 'source_type', + name: intl.formatMessage(messages.filterByValues, { value: 'source_type' }), + selectClassName: 'selectOverride', // A selector from routes/components/dataToolbar/dataToolbar.scss + selectOptions: [ + { + key: 'AWS', + name: intl.formatMessage(messages.aws), + }, + { + key: 'Azure', + name: intl.formatMessage(messages.azure), + }, + { + key: 'GCP', + name: intl.formatMessage(messages.gcp), + }, + // { + // key: 'IBM', + // name: intl.formatMessage(messages.ibm), // Todo: enable when supported by API + // }, + { + key: 'OCI', + name: intl.formatMessage(messages.oci), + }, + { + key: 'OCP', + name: intl.formatMessage(messages.openShift), + }, + ], + }, + { + ariaLabelKey: 'status', + placeholderKey: 'status', + key: 'enabled', + name: intl.formatMessage(messages.filterByValues, { value: 'status' }), + selectOptions: [ + { + name: intl.formatMessage(messages.enabled), + key: 'true', + }, + { + name: intl.formatMessage(messages.disabled), + key: 'false', + }, + ], + }, + ]; + return options; + }; + + public render() { + const { + canWrite, + isAllSelected, + isDisabled, + itemsPerPage, + itemsTotal, + onFilterAdded, + onFilterRemoved, + pagination, + query, + } = this.props; + const { categoryOptions } = this.state; + + return ( + + ); + } +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const mapStateToProps = createMapStateToProps(() => { + return { + // TBD... + }; +}); + +const mapDispatchToProps: TagMappingsToolbarDispatchProps = { + // TBD... +}; + +const TagMappingsToolbarConnect = connect(mapStateToProps, mapDispatchToProps)(TagMappingsToolbarBase); +const TagMappingsToolbar = injectIntl(TagMappingsToolbarConnect); + +export { TagMappingsToolbar }; +export type { TagMappingsToolbarProps }; diff --git a/src/routes/settings/tagLabels/tags/index.ts b/src/routes/settings/tagLabels/tags/index.ts new file mode 100644 index 000000000..be9338902 --- /dev/null +++ b/src/routes/settings/tagLabels/tags/index.ts @@ -0,0 +1 @@ +export { default as Tags } from './tags'; diff --git a/src/routes/settings/tagLabels/tags/tags.styles.ts b/src/routes/settings/tagLabels/tags/tags.styles.ts new file mode 100644 index 000000000..690536658 --- /dev/null +++ b/src/routes/settings/tagLabels/tags/tags.styles.ts @@ -0,0 +1,14 @@ +import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; +import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; +import type React from 'react'; + +export const styles = { + action: { + marginLeft: global_spacer_md.var, + }, + pagination: { + backgroundColor: global_BackgroundColor_light_100.value, + paddingBottom: global_spacer_md.value, + paddingTop: global_spacer_md.value, + }, +} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/settings/tagLabels/tags/tags.tsx b/src/routes/settings/tagLabels/tags/tags.tsx new file mode 100644 index 000000000..207f99736 --- /dev/null +++ b/src/routes/settings/tagLabels/tags/tags.tsx @@ -0,0 +1,295 @@ +import { Pagination, PaginationVariant } from '@patternfly/react-core'; +import type { Query } from 'api/queries/query'; +import { getQuery } from 'api/queries/query'; +import type { Settings, SettingsData } from 'api/settings'; +import { SettingsType } from 'api/settings'; +import type { AxiosError } from 'axios'; +import messages from 'locales/messages'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import { NotAvailable } from 'routes/components/page/notAvailable'; +import { LoadingState } from 'routes/components/state/loadingState'; +import * as queryUtils from 'routes/utils/query'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { settingsActions, settingsSelectors } from 'store/settings'; +import { useStateCallback } from 'utils/hooks'; + +import { styles } from './tags.styles'; +import { TagsTable } from './tagsTable'; +import { TagsToolbar } from './tagsToolbar'; + +interface TagsOwnProps { + canWrite?: boolean; +} + +export interface TagsMapProps { + query?: Query; +} + +export interface TagsStateProps { + settings?: Settings; + settingsError?: AxiosError; + settingsStatus?: FetchStatus; + settingsQueryString?: string; +} + +type TagsProps = TagsOwnProps; + +const baseQuery: Query = { + limit: 10, + offset: 0, + filter_by: {}, + order_by: { + key: 'asc', + }, +}; + +const Tags: React.FC = ({ canWrite }) => { + const [query, setQuery] = useState({ ...baseQuery }); + const [selectedItems, setSelectedItems] = useStateCallback([]); + const dispatch: ThunkDispatch = useDispatch(); + const intl = useIntl(); + + const { settings, settingsError, settingsStatus } = useMapToProps({ query }); + + const getTags = () => { + if (settings) { + return settings.data as any; + } + return []; + }; + + const getPagination = (isDisabled = false, isBottom = false) => { + const count = settings?.meta ? settings.meta.count : 0; + const limit = settings?.meta ? settings.meta.limit : baseQuery.limit; + const offset = settings?.meta ? settings.meta.offset : baseQuery.offset; + const page = Math.trunc(offset / limit + 1); + + return ( + handleOnPerPageSelect(perPage)} + onSetPage={(event, pageNumber) => handleOnSetPage(pageNumber)} + page={page} + perPage={limit} + titles={{ + paginationAriaLabel: intl.formatMessage(messages.paginationTitle, { + title: intl.formatMessage(messages.openShift), + placement: isBottom ? 'bottom' : 'top', + }), + }} + variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} + widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + /> + ); + }; + + const getTable = () => { + return ( + handleOnSort(sortType, isSortAscending)} + settings={settings} + selectedItems={selectedItems} + /> + ); + }; + + const getToolbar = (tags: SettingsData[]) => { + const hasEnabledItem = selectedItems.find(item => item.enabled); + const hasDisabledItem = selectedItems.find(item => !item.enabled); + const itemsTotal = settings?.meta ? settings.meta.count : 0; + const enabledTagsCount = settings?.meta ? settings.meta.enabled_tags_count : 0; + const enabledTagsLimit = settings?.meta ? settings.meta.enabled_tags_limit : 0; + + return ( + handleOnFilterAdded(filter)} + onFilterRemoved={filter => handleOnFilterRemoved(filter)} + pagination={getPagination(isDisabled)} + query={query} + selectedItems={selectedItems} + showBulkSelectAll={false} + /> + ); + }; + + const handleOnBulkSelect = (action: string) => { + if (action === 'none') { + setSelectedItems([]); + } else if (action === 'page') { + const newSelectedItems = [...selectedItems]; + getTags().map(val => { + if (!newSelectedItems.find(item => item.uuid === val.uuid)) { + newSelectedItems.push(val); + } + }); + setSelectedItems(newSelectedItems); + } + }; + + const handleOnDisableTags = () => { + if (selectedItems.length > 0) { + setSelectedItems([], () => { + dispatch( + settingsActions.updateSettings(SettingsType.tagsDisable, { + ids: selectedItems.map(item => item.uuid), + }) + ); + }); + } + }; + + const handleOnEnableTags = () => { + if (selectedItems.length > 0) { + setSelectedItems([], () => { + dispatch( + settingsActions.updateSettings(SettingsType.tagsEnable, { + ids: selectedItems.map(item => item.uuid), + }) + ); + }); + } + }; + + const handleOnFilterAdded = filter => { + const newQuery = queryUtils.handleOnFilterAdded(query, filter); + setQuery(newQuery); + }; + + const handleOnFilterRemoved = filter => { + const newQuery = queryUtils.handleOnFilterRemoved(query, filter); + setQuery(newQuery); + }; + + const handleOnPerPageSelect = perPage => { + const newQuery = queryUtils.handleOnPerPageSelect(query, perPage, true); + setQuery(newQuery); + }; + + const handleOnSetPage = pageNumber => { + const newQuery = queryUtils.handleOnSetPage(query, settings, pageNumber, true); + setQuery(newQuery); + }; + + const handleonSelect = (items: SettingsData[], isSelected: boolean = false) => { + let newItems = [...selectedItems]; + if (items && items.length > 0) { + if (isSelected) { + items.map(item => newItems.push(item)); + } else { + items.map(item => { + newItems = newItems.filter(val => val.uuid !== item.uuid); + }); + } + } + setSelectedItems(newItems); + }; + + const handleOnSort = (sortType, isSortAscending) => { + const newQuery = queryUtils.handleOnSort(query, sortType, isSortAscending); + setQuery(newQuery); + }; + + if (settingsError) { + return ; + } + + const tags = getTags(); + const isDisabled = tags.length === 0; + const enabledTagsLimit = settings?.meta ? settings.meta.enabled_tags_limit : 0; + + return ( + <> +
+ {intl.formatMessage(messages.tagDesc, { + count: enabledTagsLimit, + learnMore: ( + + {intl.formatMessage(messages.learnMore)} + + ), + })} +
+ {getToolbar(tags)} + {settingsStatus === FetchStatus.inProgress ? ( + + ) : ( + <> + {getTable()} +
{getPagination(isDisabled, true)}
+ + )} + + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = ({ query }: TagsMapProps): TagsStateProps => { + const dispatch: ThunkDispatch = useDispatch(); + + const settingsQuery = { + filter_by: query.filter_by, + limit: query.limit, + offset: query.offset, + order_by: query.order_by, + }; + const settingsQueryString = getQuery(settingsQuery); + const settings = useSelector((state: RootState) => + settingsSelectors.selectSettings(state, SettingsType.tags, settingsQueryString) + ); + const settingsStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsStatus(state, SettingsType.tags, settingsQueryString) + ); + const settingsError = useSelector((state: RootState) => + settingsSelectors.selectSettingsError(state, SettingsType.tags, settingsQueryString) + ); + + const settingsUpdateDisableStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsDisable) + ); + const settingsUpdateEnableStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsEnable) + ); + + useEffect(() => { + if ( + !settingsError && + settingsStatus !== FetchStatus.inProgress && + settingsUpdateDisableStatus !== FetchStatus.inProgress && + settingsUpdateEnableStatus !== FetchStatus.inProgress + ) { + dispatch(settingsActions.fetchSettings(SettingsType.tags, settingsQueryString)); + } + }, [query, settingsUpdateDisableStatus, settingsUpdateEnableStatus]); + + return { + settings, + settingsError, + settingsStatus, + settingsQueryString, + }; +}; + +export default Tags; diff --git a/src/routes/settings/tagLabels/tagTable.tsx b/src/routes/settings/tagLabels/tags/tagsTable.tsx similarity index 89% rename from src/routes/settings/tagLabels/tagTable.tsx rename to src/routes/settings/tagLabels/tags/tagsTable.tsx index 37444a32b..8a65d4b55 100644 --- a/src/routes/settings/tagLabels/tagTable.tsx +++ b/src/routes/settings/tagLabels/tags/tagsTable.tsx @@ -11,7 +11,7 @@ import type { ComputedReportItem } from 'routes/utils/computedReport/getComputed import type { RouterComponentProps } from 'utils/router'; import { withRouter } from 'utils/router'; -interface TagTableOwnProps extends RouterComponentProps, WrappedComponentProps { +interface TagsTableOwnProps extends RouterComponentProps, WrappedComponentProps { canWrite?: boolean; filterBy?: any; isAllSelected?: boolean; @@ -23,15 +23,15 @@ interface TagTableOwnProps extends RouterComponentProps, WrappedComponentProps { settings: Settings; } -interface TagTableState { +interface TagsTableState { columns?: any[]; rows?: any[]; } -type TagTableProps = TagTableOwnProps; +type TagsTableProps = TagsTableOwnProps; -class TagTableBase extends React.Component { - public state: TagTableState = { +class TagsTableBase extends React.Component { + public state: TagsTableState = { columns: [], rows: [], }; @@ -40,7 +40,7 @@ class TagTableBase extends React.Component { this.initDatum(); } - public componentDidUpdate(prevProps: TagTableProps) { + public componentDidUpdate(prevProps: TagsTableProps) { const { selectedItems, settings } = this.props; const currentReport = settings?.data ? JSON.stringify(settings.data) : ''; const previousReport = prevProps?.settings.data ? JSON.stringify(prevProps.settings.data) : ''; @@ -125,6 +125,7 @@ class TagTableBase extends React.Component { columns={columns} filterBy={filterBy} isLoading={isLoading} + isSelectable onSelect={onSelect} onSort={onSort} orderBy={orderBy} @@ -135,6 +136,6 @@ class TagTableBase extends React.Component { } } -const TagTable = injectIntl(withRouter(TagTableBase)); +const TagsTable = injectIntl(withRouter(TagsTableBase)); -export { TagTable }; +export { TagsTable }; diff --git a/src/routes/settings/tagLabels/tagToolbar.tsx b/src/routes/settings/tagLabels/tags/tagsToolbar.tsx similarity index 87% rename from src/routes/settings/tagLabels/tagToolbar.tsx rename to src/routes/settings/tagLabels/tags/tagsToolbar.tsx index 0db966184..5b5b5e6a9 100644 --- a/src/routes/settings/tagLabels/tagToolbar.tsx +++ b/src/routes/settings/tagLabels/tags/tagsToolbar.tsx @@ -12,9 +12,9 @@ import type { ComputedReportItem } from 'routes/utils/computedReport/getComputed import type { Filter } from 'routes/utils/filter'; import { createMapStateToProps } from 'store/common'; -import { styles } from './tagLabels.styles'; +import { styles } from './tags.styles'; -interface TagToolbarOwnProps { +interface TagsToolbarOwnProps { canWrite?: boolean; enabledTagsCount?: number; enabledTagsLimit?: number; @@ -35,23 +35,23 @@ interface TagToolbarOwnProps { showBulkSelectAll?: boolean; } -interface TagToolbarStateProps { +interface TagsToolbarStateProps { // TBD... } -interface TagToolbarDispatchProps { +interface TagsToolbarDispatchProps { // TBD... } -interface TagToolbarState { +interface TagsToolbarState { categoryOptions?: ToolbarChipGroupExt[]; } -type TagToolbarProps = TagToolbarOwnProps & TagToolbarStateProps & TagToolbarDispatchProps & WrappedComponentProps; +type TagsToolbarProps = TagsToolbarOwnProps & TagsToolbarStateProps & TagsToolbarDispatchProps & WrappedComponentProps; -export class TagToolbarBase extends React.Component { - protected defaultState: TagToolbarState = {}; - public state: TagToolbarState = { ...this.defaultState }; +export class TagsToolbarBase extends React.Component { + protected defaultState: TagsToolbarState = {}; + public state: TagsToolbarState = { ...this.defaultState }; public componentDidMount() { this.setState({ @@ -211,18 +211,18 @@ export class TagToolbarBase extends React.Component((state, props) => { +const mapStateToProps = createMapStateToProps((state, props) => { return { // TBD... }; }); -const mapDispatchToProps: TagToolbarDispatchProps = { +const mapDispatchToProps: TagsToolbarDispatchProps = { // TBD... }; -const TagToolbarConnect = connect(mapStateToProps, mapDispatchToProps)(TagToolbarBase); -const TagToolbar = injectIntl(TagToolbarConnect); +const TagsToolbarConnect = connect(mapStateToProps, mapDispatchToProps)(TagsToolbarBase); +const TagsToolbar = injectIntl(TagsToolbarConnect); -export { TagToolbar }; -export type { TagToolbarProps }; +export { TagsToolbar }; +export type { TagsToolbarProps }; From 860118a294fc7841f0ef624b60c1343448a7c536 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Thu, 7 Mar 2024 12:20:29 -0500 Subject: [PATCH 24/61] Added empty state https://issues.redhat.com/browse/COST-3824 --- locales/data.json | 30 ++++++++ locales/translations.json | 3 + src/api/settings.ts | 2 + src/locales/messages.ts | 17 +++++ .../components/page/noData/noDataState.tsx | 2 +- .../noOptimizations/noOptimizationsState.tsx | 2 +- .../page/noProviders/noProvidersState.tsx | 2 +- .../emptyFilterState/emptyFilterState.tsx | 2 +- .../state/errorState/errorState.tsx | 2 +- .../state/loadingState/loadingState.tsx | 2 +- .../costModels/components/errorState.tsx | 2 +- .../costModels/costModel/costModelInfo.tsx | 2 +- .../costModels/costModel/priceListTable.tsx | 2 +- .../settings/costModels/costModel/table.tsx | 2 +- .../costModelWizard/priceListTable.tsx | 2 +- .../costModels/costModelWizard/review.tsx | 2 +- .../costModelsDetails/emptyStateBase.tsx | 2 +- .../tagMappings/tagMappings.styles.ts | 3 + .../tagLabels/tagMappings/tagMappings.tsx | 50 ++++++------- .../tagMappings/tagMappingsEmptyState.tsx | 73 +++++++++++++++++++ src/routes/settings/tagLabels/tags/tags.tsx | 4 +- 21 files changed, 165 insertions(+), 43 deletions(-) create mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx diff --git a/locales/data.json b/locales/data.json index 80fabf3f3..effa863db 100644 --- a/locales/data.json +++ b/locales/data.json @@ -10205,6 +10205,36 @@ "value": "There are no export files available" } ], + "noMappedTags": [ + { + "type": 0, + "value": "No mapped tags" + } + ], + "noMappedTagsDesc": [ + { + "type": 0, + "value": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. " + }, + { + "type": 1, + "value": "warning" + }, + { + "type": 0, + "value": " Changes will be reflected within 24 hours. " + }, + { + "type": 1, + "value": "learnMore" + } + ], + "noMappedTagsWarning": [ + { + "type": 0, + "value": "Tags must be enabled to be mapped." + } + ], "noOptimizationsDesc": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index d3f1cd710..08ef744cf 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -374,6 +374,9 @@ "noDataStateRefresh": "Refresh this page", "noDataStateTitle": "Still processing the data", "noExportsStateTitle": "There are no export files available", + "noMappedTags": "No mapped tags", + "noMappedTagsDesc": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. {warning} Changes will be reflected within 24 hours. {learnMore}", + "noMappedTagsWarning": "Tags must be enabled to be mapped.", "noOptimizationsDesc": "Resource Optimization is now available in preview for select customers. If your organization wants to participate, tell us through the Feedback button, which is purple and located on the right. Otherwise, there is not enough data available to generate an optimization.", "noOptimizationsTitle": "No optimizations available", "noProvidersStateAwsDesc": "Add an Amazon Web Services account to see a total cost breakdown of your spend by accounts, organizational units, services, regions, or tags.", diff --git a/src/api/settings.ts b/src/api/settings.ts index 372dfe060..e3ab737ad 100644 --- a/src/api/settings.ts +++ b/src/api/settings.ts @@ -40,6 +40,7 @@ export const enum SettingsType { tags = 'tags', tagsEnable = 'tagsEnable', tagsDisable = 'tagsDisable', + tagsMappings = 'tagsMappings', } export const SettingsTypePaths: Partial> = { @@ -52,6 +53,7 @@ export const SettingsTypePaths: Partial> = { [SettingsType.tags]: 'settings/tags', [SettingsType.tagsEnable]: 'settings/tags/enable/', [SettingsType.tagsDisable]: 'settings/tags/disable/', + [SettingsType.tagsMappings]: 'settings/tags/mappings', }; export function fetchSettings(settingsType: SettingsType, query: string) { diff --git a/src/locales/messages.ts b/src/locales/messages.ts index aa6ee0567..c605d0716 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -2418,6 +2418,23 @@ export default defineMessages({ description: 'There are no export files available', id: 'noExportsStateTitle', }, + noMappedTags: { + defaultMessage: 'No mapped tags', + description: 'No mapped tags', + id: 'noMappedTags', + }, + noMappedTagsDesc: { + defaultMessage: + 'Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. {warning} Changes will be reflected within 24 hours. {learnMore}', + description: + 'Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. {warning} Changes will be reflected within 24 hours. {learnMore}', + id: 'noMappedTagsDesc', + }, + noMappedTagsWarning: { + defaultMessage: 'Tags must be enabled to be mapped.', + description: 'Tags must be enabled to be mapped.', + id: 'noMappedTagsWarning', + }, noOptimizationsDesc: { defaultMessage: 'Resource Optimization is now available in preview for select customers. If your organization wants to participate, tell us through the Feedback button, which is purple and located on the right. Otherwise, there is not enough data available to generate an optimization.', diff --git a/src/routes/components/page/noData/noDataState.tsx b/src/routes/components/page/noData/noDataState.tsx index 6912fff87..1a3c05911 100644 --- a/src/routes/components/page/noData/noDataState.tsx +++ b/src/routes/components/page/noData/noDataState.tsx @@ -26,7 +26,7 @@ class NoDataStateBase extends React.Component { return ( {intl.formatMessage(messages.noDataStateTitle)}} + titleText={intl.formatMessage(messages.noDataStateTitle)} icon={} headingLevel="h5" /> diff --git a/src/routes/components/page/noOptimizations/noOptimizationsState.tsx b/src/routes/components/page/noOptimizations/noOptimizationsState.tsx index 771c8275f..4b78a7148 100644 --- a/src/routes/components/page/noOptimizations/noOptimizationsState.tsx +++ b/src/routes/components/page/noOptimizations/noOptimizationsState.tsx @@ -24,7 +24,7 @@ class NoOptimizationsStateBase extends React.Component {intl.formatMessage(messages.noOptimizationsTitle)}} + titleText={intl.formatMessage(messages.noOptimizationsTitle)} icon={} headingLevel="h1" /> diff --git a/src/routes/components/page/noProviders/noProvidersState.tsx b/src/routes/components/page/noProviders/noProvidersState.tsx index 175364ee1..c944ef6c5 100644 --- a/src/routes/components/page/noProviders/noProvidersState.tsx +++ b/src/routes/components/page/noProviders/noProvidersState.tsx @@ -84,7 +84,7 @@ class NoProvidersStateBase extends React.Component { return ( {intl.formatMessage(titleKey)}} + titleText={intl.formatMessage(titleKey)} icon={} headingLevel="h1" /> diff --git a/src/routes/components/state/emptyFilterState/emptyFilterState.tsx b/src/routes/components/state/emptyFilterState/emptyFilterState.tsx index 9f88006dd..7370aec93 100644 --- a/src/routes/components/state/emptyFilterState/emptyFilterState.tsx +++ b/src/routes/components/state/emptyFilterState/emptyFilterState.tsx @@ -114,7 +114,7 @@ const EmptyFilterStateBase: React.FC = ({ > {getItem()} - {intl.formatMessage(title)}} headingLevel="h2" /> + {intl.formatMessage(subTitle)}
diff --git a/src/routes/components/state/errorState/errorState.tsx b/src/routes/components/state/errorState/errorState.tsx index 9daa76d01..fc3f60cf1 100644 --- a/src/routes/components/state/errorState/errorState.tsx +++ b/src/routes/components/state/errorState/errorState.tsx @@ -30,7 +30,7 @@ const ErrorStateBase: React.FC = ({ error, icon = ErrorCircleOI return ( - {title}} icon={} headingLevel="h5" /> + } headingLevel="h5" /> {subTitle} ); diff --git a/src/routes/components/state/loadingState/loadingState.tsx b/src/routes/components/state/loadingState/loadingState.tsx index bb3af4d76..0ae19d140 100644 --- a/src/routes/components/state/loadingState/loadingState.tsx +++ b/src/routes/components/state/loadingState/loadingState.tsx @@ -20,7 +20,7 @@ const LoadingStateBase: React.FC = ({ return ( - {heading}} headingLevel="h5" /> + {body} ); diff --git a/src/routes/settings/costModels/components/errorState.tsx b/src/routes/settings/costModels/components/errorState.tsx index ff767b950..3417c2563 100644 --- a/src/routes/settings/costModels/components/errorState.tsx +++ b/src/routes/settings/costModels/components/errorState.tsx @@ -34,7 +34,7 @@ export const ErrorState: React.FC = ({ variant, actionButton, t return ( {title}} + titleText={title} icon={} headingLevel="h4" /> diff --git a/src/routes/settings/costModels/costModel/costModelInfo.tsx b/src/routes/settings/costModels/costModel/costModelInfo.tsx index aa2b07e09..2eb9c3afa 100644 --- a/src/routes/settings/costModels/costModel/costModelInfo.tsx +++ b/src/routes/settings/costModels/costModel/costModelInfo.tsx @@ -93,7 +93,7 @@ class CostModelInfo extends React.Component {intl.formatMessage(messages.costModelsUUIDEmptyState)}} + titleText={intl.formatMessage(messages.costModelsUUIDEmptyState)} icon={} headingLevel="h2" /> diff --git a/src/routes/settings/costModels/costModel/priceListTable.tsx b/src/routes/settings/costModels/costModel/priceListTable.tsx index 82d3e646c..8355ed9a2 100644 --- a/src/routes/settings/costModels/costModel/priceListTable.tsx +++ b/src/routes/settings/costModels/costModel/priceListTable.tsx @@ -293,7 +293,7 @@ class PriceListTable extends React.Component {intl.formatMessage(messages.priceListEmptyRate)}} + titleText={intl.formatMessage(messages.priceListEmptyRate)} icon={} headingLevel="h2" /> diff --git a/src/routes/settings/costModels/costModel/table.tsx b/src/routes/settings/costModels/costModel/table.tsx index ae289c940..efab2048d 100644 --- a/src/routes/settings/costModels/costModel/table.tsx +++ b/src/routes/settings/costModels/costModel/table.tsx @@ -126,7 +126,7 @@ class TableBase extends React.Component {
{intl.formatMessage(messages.costModelsSourceEmptyStateDesc)}} + titleText={intl.formatMessage(messages.costModelsSourceEmptyStateDesc)} icon={} headingLevel="h2" /> diff --git a/src/routes/settings/costModels/costModelWizard/priceListTable.tsx b/src/routes/settings/costModels/costModelWizard/priceListTable.tsx index 2baa4ec7e..cd75a9422 100644 --- a/src/routes/settings/costModels/costModelWizard/priceListTable.tsx +++ b/src/routes/settings/costModels/costModelWizard/priceListTable.tsx @@ -94,7 +94,7 @@ class PriceListTable extends React.Component {intl.formatMessage(messages.costModelsWizardEmptyStateTitle)}} + titleText={intl.formatMessage(messages.costModelsWizardEmptyStateTitle)} icon={} headingLevel="h2" /> diff --git a/src/routes/settings/costModels/costModelWizard/review.tsx b/src/routes/settings/costModels/costModelWizard/review.tsx index 2d064c1ba..338fee686 100644 --- a/src/routes/settings/costModels/costModelWizard/review.tsx +++ b/src/routes/settings/costModels/costModelWizard/review.tsx @@ -36,7 +36,7 @@ const ReviewSuccessBase: React.FC = ({ intl }) => ( {({ onClose, name }) => ( {intl.formatMessage(messages.costModelsWizardReviewStatusTitle)}} + titleText={intl.formatMessage(messages.costModelsWizardReviewStatusTitle)} icon={ diff --git a/src/routes/settings/costModels/costModelsDetails/emptyStateBase.tsx b/src/routes/settings/costModels/costModelsDetails/emptyStateBase.tsx index b0b78d7a2..45f032b9d 100644 --- a/src/routes/settings/costModels/costModelsDetails/emptyStateBase.tsx +++ b/src/routes/settings/costModels/costModelsDetails/emptyStateBase.tsx @@ -11,7 +11,7 @@ interface EmptyStateBaseProps { function EmptyStateBase(props: EmptyStateBaseProps): JSX.Element { return ( - {props.title}} icon={} headingLevel="h2" /> + } headingLevel="h2" /> {props.description} {props.actions ? props.actions : null} diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts b/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts index 690536658..01ecb7545 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts +++ b/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts @@ -6,6 +6,9 @@ export const styles = { action: { marginLeft: global_spacer_md.var, }, + emptyStateContainer: { + paddingTop: global_spacer_md.value, + }, pagination: { backgroundColor: global_BackgroundColor_light_100.value, paddingBottom: global_spacer_md.value, diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx index e7fa8a531..13dd6de8b 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx @@ -1,3 +1,4 @@ +import Unavailable from '@patternfly/react-component-groups/dist/esm/UnavailableContent'; import { Pagination, PaginationVariant } from '@patternfly/react-core'; import type { Query } from 'api/queries/query'; import { getQuery } from 'api/queries/query'; @@ -10,7 +11,6 @@ import { useIntl } from 'react-intl'; import { useDispatch, useSelector } from 'react-redux'; import type { AnyAction } from 'redux'; import type { ThunkDispatch } from 'redux-thunk'; -import { NotAvailable } from 'routes/components/page/notAvailable'; import { LoadingState } from 'routes/components/state/loadingState'; import * as queryUtils from 'routes/utils/query'; import type { RootState } from 'store'; @@ -18,6 +18,7 @@ import { FetchStatus } from 'store/common'; import { settingsActions, settingsSelectors } from 'store/settings'; import { styles } from './tagMappings.styles'; +import { TagMappingsEmptyState } from './tagMappingsEmptyState'; import { TagMappingsTable } from './tagMappingsTable'; import { TagMappingsToolbar } from './tagMappingsToolbar'; @@ -43,7 +44,7 @@ const baseQuery: Query = { offset: 0, filter_by: {}, order_by: { - key: 'asc', + parent: 'asc', }, }; @@ -100,14 +101,14 @@ const TagMappings: React.FC = ({ canWrite }) => { ); }; - const getToolbar = (tags: SettingsData[]) => { + const getToolbar = (mappings: SettingsData[]) => { const itemsTotal = settings?.meta ? settings.meta.count : 0; return ( handleOnFilterAdded(filter)} @@ -148,11 +149,12 @@ const TagMappings: React.FC = ({ canWrite }) => { }; if (settingsError) { - return ; + return ; } - const tags = getMappings(); - const isDisabled = tags.length === 0; + const mappings = getMappings(); + const isDisabled = mappings.length === 0; + const hasMappings = mappings.length > 0 && !Object.keys(query.filter_by).length; // no filter applied return ( <> @@ -166,14 +168,18 @@ const TagMappings: React.FC = ({ canWrite }) => { warning: {intl.formatMessage(messages.tagMappingWarning)}, })}
- {getToolbar(tags)} + {hasMappings && getToolbar(mappings)} {settingsStatus === FetchStatus.inProgress ? ( - ) : ( + ) : hasMappings ? ( <> {getTable()}
{getPagination(isDisabled, true)}
+ ) : ( +
+ +
)} ); @@ -191,32 +197,20 @@ const useMapToProps = ({ query }: MappingsMapProps): MappingsStateProps => { }; const settingsQueryString = getQuery(settingsQuery); const settings = useSelector((state: RootState) => - settingsSelectors.selectSettings(state, SettingsType.tags, settingsQueryString) + settingsSelectors.selectSettings(state, SettingsType.tagsMappings, settingsQueryString) ); const settingsStatus = useSelector((state: RootState) => - settingsSelectors.selectSettingsStatus(state, SettingsType.tags, settingsQueryString) + settingsSelectors.selectSettingsStatus(state, SettingsType.tagsMappings, settingsQueryString) ); const settingsError = useSelector((state: RootState) => - settingsSelectors.selectSettingsError(state, SettingsType.tags, settingsQueryString) - ); - - const settingsUpdateDisableStatus = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsDisable) - ); - const settingsUpdateEnableStatus = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsEnable) + settingsSelectors.selectSettingsError(state, SettingsType.tagsMappings, settingsQueryString) ); useEffect(() => { - if ( - !settingsError && - settingsStatus !== FetchStatus.inProgress && - settingsUpdateDisableStatus !== FetchStatus.inProgress && - settingsUpdateEnableStatus !== FetchStatus.inProgress - ) { - dispatch(settingsActions.fetchSettings(SettingsType.tags, settingsQueryString)); + if (!settingsError && settingsStatus !== FetchStatus.inProgress) { + dispatch(settingsActions.fetchSettings(SettingsType.tagsMappings, settingsQueryString)); } - }, [query, settingsUpdateDisableStatus, settingsUpdateEnableStatus]); + }, [query]); return { settings, diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx new file mode 100644 index 000000000..61fd1038b --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx @@ -0,0 +1,73 @@ +import { + Button, + ButtonVariant, + EmptyState, + EmptyStateActions, + EmptyStateBody, + EmptyStateFooter, + EmptyStateHeader, + EmptyStateIcon, + EmptyStateVariant, + Tooltip, +} from '@patternfly/react-core'; +import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon'; +import messages from 'locales/messages'; +import React from 'react'; +import { useIntl } from 'react-intl'; + +interface TagMappingsEmptyStateOwnProps { + canWrite?: boolean; + isDisabled?: boolean; + onCreateTagMapping(); +} + +type TagMappingsEmptyStateProps = TagMappingsEmptyStateOwnProps; + +const TagMappingsEmptyState: React.FC = ({ + canWrite, + isDisabled, + onCreateTagMapping, +}: TagMappingsEmptyStateOwnProps) => { + const intl = useIntl(); + + const getActions = () => { + const getTooltip = children => { + if (!canWrite) { + const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); + return {children}; + } + return children; + }; + + return getTooltip( + + ); + }; + + return ( + + } + headingLevel="h5" + /> + + {intl.formatMessage(messages.noMappedTagsDesc, { + learnMore: ( + + {intl.formatMessage(messages.learnMore)} + + ), + warning: {intl.formatMessage(messages.noMappedTagsWarning)}, + })} + + + {getActions()} + + + ); +}; + +export { TagMappingsEmptyState }; diff --git a/src/routes/settings/tagLabels/tags/tags.tsx b/src/routes/settings/tagLabels/tags/tags.tsx index 207f99736..2c5edeca2 100644 --- a/src/routes/settings/tagLabels/tags/tags.tsx +++ b/src/routes/settings/tagLabels/tags/tags.tsx @@ -1,3 +1,4 @@ +import Unavailable from '@patternfly/react-component-groups/dist/esm/UnavailableContent'; import { Pagination, PaginationVariant } from '@patternfly/react-core'; import type { Query } from 'api/queries/query'; import { getQuery } from 'api/queries/query'; @@ -10,7 +11,6 @@ import { useIntl } from 'react-intl'; import { useDispatch, useSelector } from 'react-redux'; import type { AnyAction } from 'redux'; import type { ThunkDispatch } from 'redux-thunk'; -import { NotAvailable } from 'routes/components/page/notAvailable'; import { LoadingState } from 'routes/components/state/loadingState'; import * as queryUtils from 'routes/utils/query'; import type { RootState } from 'store'; @@ -213,7 +213,7 @@ const Tags: React.FC = ({ canWrite }) => { }; if (settingsError) { - return ; + return ; } const tags = getTags(); From 480ea1deda7870df53f828c05c86c7287e39e11a Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Thu, 7 Mar 2024 20:53:44 -0500 Subject: [PATCH 25/61] Added initial wizard and success empty state https://issues.redhat.com/browse/COST-3824 --- locales/data.json | 52 ++++++- locales/translations.json | 12 +- src/locales/messages.ts | 52 ++++++- .../clusterInfo/cloudIntegration.tsx | 2 +- .../clusterInfo/clusterContent.tsx | 2 +- ...{modal.styles.ts => clusterInfo.styles.ts} | 0 .../ocpBreakdown/clusterInfo/clusterInfo.tsx | 6 +- .../tagLabels/tagMappings/tagMappings.tsx | 11 +- .../tagMappings/tagMappingsEmptyState.tsx | 27 +--- .../tagMappings/tagMappingsToolbar.tsx | 24 +--- .../tagMappingsWizard/cloudIntegration.tsx | 108 +++++++++++++++ .../tagLabels/tagMappingsWizard/index.ts | 1 + .../tagMappingsWizard.styles.ts | 22 +++ .../tagMappingsWizard/tagMappingsWizard.tsx | 128 ++++++++++++++++++ .../tagMappingsWizardEmptyState.tsx | 69 ++++++++++ 15 files changed, 452 insertions(+), 64 deletions(-) rename src/routes/details/ocpBreakdown/clusterInfo/{modal.styles.ts => clusterInfo.styles.ts} (100%) create mode 100644 src/routes/settings/tagLabels/tagMappingsWizard/cloudIntegration.tsx create mode 100644 src/routes/settings/tagLabels/tagMappingsWizard/index.ts create mode 100644 src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.styles.ts create mode 100644 src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.tsx create mode 100644 src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizardEmptyState.tsx diff --git a/locales/data.json b/locales/data.json index effa863db..b9864ca2a 100644 --- a/locales/data.json +++ b/locales/data.json @@ -11944,7 +11944,7 @@ "value": "Map tags and labels" } ], - "tagMappingDesc": [ + "tagMappingsDesc": [ { "type": 0, "value": "Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. " @@ -11962,12 +11962,60 @@ "value": "learnMore" } ], - "tagMappingWarning": [ + "tagMappingsWarning": [ { "type": 0, "value": "You must enable tags to use tag mapping." } ], + "tagMappingsWizardDesc": [ + { + "type": 0, + "value": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours." + } + ], + "tagMappingsWizardNavToCreateTagMapping": [ + { + "type": 0, + "value": "Create another tag mapping" + } + ], + "tagMappingsWizardNavToTagMappings": [ + { + "type": 0, + "value": "Go back to Cost Management Settings" + } + ], + "tagMappingsWizardReview": [ + { + "type": 0, + "value": "Review details" + } + ], + "tagMappingsWizardSelectChildTags": [ + { + "type": 0, + "value": "Select child tags" + } + ], + "tagMappingsWizardSelectParentTag": [ + { + "type": 0, + "value": "Select parent tag" + } + ], + "tagMappingsWizardSuccess": [ + { + "type": 0, + "value": "Tag mapping successful" + } + ], + "tagMappingsWizardSuccessDesc": [ + { + "type": 0, + "value": "Your tag keys were successfully mapped. Changes will be reflected in report summarizations within 24 hours." + } + ], "tagNames": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 08ef744cf..09d03bfdd 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -543,8 +543,16 @@ "tagLabels": "Tags and labels", "tagLabelsEnable": "Enable tags and labels", "tagLabelsMap": "Map tags and labels", - "tagMappingDesc": "Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}", - "tagMappingWarning": "You must enable tags to use tag mapping.", + "tagMappingsDesc": "Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}", + "tagMappingsWarning": "You must enable tags to use tag mapping.", + "tagMappingsWizardDesc": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours.", + "tagMappingsWizardNavToCreateTagMapping": "Create another tag mapping", + "tagMappingsWizardNavToTagMappings": "Go back to Cost Management Settings", + "tagMappingsWizardReview": "Review details", + "tagMappingsWizardSelectChildTags": "Select child tags", + "tagMappingsWizardSelectParentTag": "Select parent tag", + "tagMappingsWizardSuccess": "Tag mapping successful", + "tagMappingsWizardSuccessDesc": "Your tag keys were successfully mapped. Changes will be reflected in report summarizations within 24 hours.", "tagNames": "Tag names", "timeOfExport": "Time of export", "to": "to", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index c605d0716..1ef54354f 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -3339,17 +3339,61 @@ export default defineMessages({ description: 'Tags and labels', id: 'tagLabels', }, - tagMappingDesc: { + tagMappingsDesc: { defaultMessage: 'Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}', description: 'Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}', - id: 'tagMappingDesc', + id: 'tagMappingsDesc', }, - tagMappingWarning: { + tagMappingsWarning: { defaultMessage: 'You must enable tags to use tag mapping.', description: 'You must enable tags to use tag mapping.', - id: 'tagMappingWarning', + id: 'tagMappingsWarning', + }, + tagMappingsWizardDesc: { + defaultMessage: + 'Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours.', + description: + 'Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours.', + id: 'tagMappingsWizardDesc', + }, + tagMappingsWizardNavToCreateTagMapping: { + defaultMessage: 'Create another tag mapping', + description: 'Create another tag mapping', + id: 'tagMappingsWizardNavToCreateTagMapping', + }, + tagMappingsWizardNavToTagMappings: { + defaultMessage: 'Go back to Cost Management Settings', + description: 'Go back to Cost Management Settings', + id: 'tagMappingsWizardNavToTagMappings', + }, + tagMappingsWizardReview: { + defaultMessage: 'Review details', + description: 'Review details', + id: 'tagMappingsWizardReview', + }, + tagMappingsWizardSelectChildTags: { + defaultMessage: 'Select child tags', + description: 'Select child tags', + id: 'tagMappingsWizardSelectChildTags', + }, + tagMappingsWizardSelectParentTag: { + defaultMessage: 'Select parent tag', + description: 'Select parent tag', + id: 'tagMappingsWizardSelectParentTag', + }, + tagMappingsWizardSuccess: { + defaultMessage: 'Tag mapping successful', + description: 'Tag mapping successful', + id: 'tagMappingsWizardSuccess', + }, + tagMappingsWizardSuccessDesc: { + defaultMessage: + 'Your tag keys were successfully mapped. Changes will be reflected in report summarizations within 24 hours.', + description: + 'Your tag keys were successfully mapped. Changes will be reflected in report summarizations within 24 hours.', + id: 'tagMappingsWizardSuccessDesc', }, tagNames: { defaultMessage: 'Tag names', diff --git a/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx index 3c09ef270..b5ecf9313 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx @@ -16,7 +16,7 @@ import { FetchStatus } from 'store/common'; import { providersActions, providersSelectors } from 'store/providers'; import { formatPath, getReleasePath } from 'utils/paths'; -import { styles } from './modal.styles'; +import { styles } from './clusterInfo.styles'; interface CloudIntegrationOwnProps { uuid?: string; diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx b/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx index fcfd1844a..69585f69d 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx @@ -21,7 +21,7 @@ import { providersActions, providersQuery, providersSelectors } from 'store/prov import { formatPath, getReleasePath } from 'utils/paths'; import { CloudIntegration } from './cloudIntegration'; -import { styles } from './modal.styles'; +import { styles } from './clusterInfo.styles'; interface ClusterInfoContentOwnProps { clusterId?: string; diff --git a/src/routes/details/ocpBreakdown/clusterInfo/modal.styles.ts b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts similarity index 100% rename from src/routes/details/ocpBreakdown/clusterInfo/modal.styles.ts rename to src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx index cee7f7ba9..ad2631f34 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx @@ -5,7 +5,7 @@ import React, { useState } from 'react'; import { useIntl } from 'react-intl'; import { ClusterContent } from './clusterContent'; -import { styles } from './modal.styles'; +import { styles } from './clusterInfo.styles'; interface ClusterInfoOwnProps { clusterId?: string; @@ -17,7 +17,7 @@ const ClusterInfo: React.FC = ({ clusterId }: ClusterInfoProps const intl = useIntl(); const [isOpen, setIsOpen] = useState(false); - const handleClose = () => { + const handleOnClose = () => { setIsOpen(false); }; @@ -33,7 +33,7 @@ const ClusterInfo: React.FC = ({ clusterId }: ClusterInfoProps - + diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx index 13dd6de8b..be7376e5b 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx @@ -110,7 +110,6 @@ const TagMappings: React.FC = ({ canWrite }) => { isDisabled={mappings.length === 0} itemsPerPage={mappings.length} itemsTotal={itemsTotal} - onCreateTagMapping={handleOnCreateTagMapping} onFilterAdded={filter => handleOnFilterAdded(filter)} onFilterRemoved={filter => handleOnFilterRemoved(filter)} pagination={getPagination(isDisabled)} @@ -119,10 +118,6 @@ const TagMappings: React.FC = ({ canWrite }) => { ); }; - const handleOnCreateTagMapping = () => { - // Todo: Open wizard - }; - const handleOnFilterAdded = filter => { const newQuery = queryUtils.handleOnFilterAdded(query, filter); setQuery(newQuery); @@ -159,13 +154,13 @@ const TagMappings: React.FC = ({ canWrite }) => { return ( <>
- {intl.formatMessage(messages.tagMappingDesc, { + {intl.formatMessage(messages.tagMappingsDesc, { learnMore: ( {intl.formatMessage(messages.learnMore)} ), - warning: {intl.formatMessage(messages.tagMappingWarning)}, + warning: {intl.formatMessage(messages.tagMappingsWarning)}, })}
{hasMappings && getToolbar(mappings)} @@ -178,7 +173,7 @@ const TagMappings: React.FC = ({ canWrite }) => { ) : (
- +
)} diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx index 61fd1038b..367bd3c3e 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx @@ -1,6 +1,4 @@ import { - Button, - ButtonVariant, EmptyState, EmptyStateActions, EmptyStateBody, @@ -8,17 +6,17 @@ import { EmptyStateHeader, EmptyStateIcon, EmptyStateVariant, - Tooltip, } from '@patternfly/react-core'; import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon'; import messages from 'locales/messages'; import React from 'react'; import { useIntl } from 'react-intl'; +import { TagMappingsWizard } from '../tagMappingsWizard'; + interface TagMappingsEmptyStateOwnProps { canWrite?: boolean; isDisabled?: boolean; - onCreateTagMapping(); } type TagMappingsEmptyStateProps = TagMappingsEmptyStateOwnProps; @@ -26,26 +24,9 @@ type TagMappingsEmptyStateProps = TagMappingsEmptyStateOwnProps; const TagMappingsEmptyState: React.FC = ({ canWrite, isDisabled, - onCreateTagMapping, }: TagMappingsEmptyStateOwnProps) => { const intl = useIntl(); - const getActions = () => { - const getTooltip = children => { - if (!canWrite) { - const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); - return {children}; - } - return children; - }; - - return getTooltip( - - ); - }; - return ( = ({ })} - {getActions()} + + + ); diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx index 3da2ee389..8ebf68f76 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx @@ -1,4 +1,3 @@ -import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; import type { OcpQuery } from 'api/queries/ocpQuery'; import { ResourcePathsType } from 'api/resources/resource'; import messages from 'locales/messages'; @@ -11,6 +10,8 @@ import type { ToolbarChipGroupExt } from 'routes/components/dataToolbar/utils/co import type { Filter } from 'routes/utils/filter'; import { createMapStateToProps } from 'store/common'; +import { TagMappingsWizard } from '../tagMappingsWizard'; + interface TagMappingsToolbarOwnProps { canWrite?: boolean; enabledTagsCount?: number; @@ -21,7 +22,6 @@ interface TagMappingsToolbarOwnProps { isSecondaryActionDisabled?: boolean; itemsPerPage?: number; itemsTotal?: number; - onCreateTagMapping(); onFilterAdded(filter: Filter); onFilterRemoved(filter: Filter); pagination?: React.ReactNode; @@ -55,24 +55,6 @@ export class TagMappingsToolbarBase extends React.Component { - const { canWrite, intl, isDisabled, onCreateTagMapping } = this.props; - - const getTooltip = children => { - if (!canWrite) { - const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); - return {children}; - } - return children; - }; - - return getTooltip( - - ); - }; - private getCategoryOptions = (): ToolbarChipGroupExt[] => { const { intl } = this.props; @@ -150,7 +132,7 @@ export class TagMappingsToolbarBase extends React.Component} categoryOptions={categoryOptions} isAllSelected={isAllSelected} isDisabled={isDisabled} diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/cloudIntegration.tsx b/src/routes/settings/tagLabels/tagMappingsWizard/cloudIntegration.tsx new file mode 100644 index 000000000..446a3a874 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappingsWizard/cloudIntegration.tsx @@ -0,0 +1,108 @@ +import { Text, TextList, TextListItem, TextVariants } from '@patternfly/react-core'; +import type { Provider } from 'api/providers'; +import { ProviderType } from 'api/providers'; +import type { AxiosError } from 'axios'; +import messages from 'locales/messages'; +import React, { useEffect } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import { routes } from 'routes'; +import { NotAvailable } from 'routes/components/page/notAvailable'; +import { LoadingState } from 'routes/components/state/loadingState'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { providersActions, providersSelectors } from 'store/providers'; +import { formatPath, getReleasePath } from 'utils/paths'; + +import { styles } from './tagMappingsWizard.styles'; + +interface CloudIntegrationOwnProps { + uuid?: string; +} + +export interface CloudIntegrationStateProps { + provider: Provider; + providerError: AxiosError; + providerFetchStatus: FetchStatus; + providerQueryString: string; + uuid?: string; +} + +export interface CloudIntegrationMapProps { + // TBD... +} + +type CloudIntegrationProps = CloudIntegrationOwnProps; + +const CloudIntegration: React.FC = ({ uuid }: CloudIntegrationProps) => { + const intl = useIntl(); + + const { provider, providerError, providerFetchStatus } = useMapToProps({ uuid }); + + const title = intl.formatMessage(messages.optimizations); + + if (providerError) { + return ; + } + + const release = getReleasePath(); + + if (providerFetchStatus === FetchStatus.inProgress) { + return ( +
+ +
+ ); + } + return ( + <> + {intl.formatMessage(messages.cloudIntegration)} + + + + {intl.formatMessage(messages.source, { value: provider?.source_type?.toLowerCase() })} + + {provider?.name} + + {provider?.cost_models?.length === 0 && ( + + {intl.formatMessage(messages.assignCostModel)} + + )} + + + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = ({ uuid }): CloudIntegrationStateProps => { + const dispatch: ThunkDispatch = useDispatch(); + + const providerQueryString = uuid; + const provider = useSelector( + (state: RootState) => providersSelectors.selectProviders(state, ProviderType.uuid, providerQueryString) as Provider + ); + const providerError = useSelector((state: RootState) => + providersSelectors.selectProvidersError(state, ProviderType.uuid, providerQueryString) + ); + const providerFetchStatus = useSelector((state: RootState) => + providersSelectors.selectProvidersFetchStatus(state, ProviderType.uuid, providerQueryString) + ); + + useEffect(() => { + if (!providerError && providerFetchStatus !== FetchStatus.inProgress) { + dispatch(providersActions.fetchProviders(ProviderType.uuid, providerQueryString)); + } + }, []); + + return { + provider, + providerError, + providerFetchStatus, + providerQueryString, + }; +}; + +export { CloudIntegration }; diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/index.ts b/src/routes/settings/tagLabels/tagMappingsWizard/index.ts new file mode 100644 index 000000000..fed4df454 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappingsWizard/index.ts @@ -0,0 +1 @@ +export { default as TagMappingsWizard } from './tagMappingsWizard'; diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.styles.ts b/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.styles.ts new file mode 100644 index 000000000..094bcdffa --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.styles.ts @@ -0,0 +1,22 @@ +import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; +import global_FontSize_md from '@patternfly/react-tokens/dist/js/global_FontSize_md'; +import type React from 'react'; + +export const styles = { + alert: { + marginBottom: global_FontSize_md.value, + }, + emptyState: { + margin: global_FontSize_md.value, + }, + icon: { + margin: global_FontSize_md.value, + }, + loading: { + backgroundColor: global_BackgroundColor_light_100.value, + minHeight: '520px', + }, + spacing: { + marginRight: global_FontSize_md.value, + }, +} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.tsx b/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.tsx new file mode 100644 index 000000000..f61ba4d14 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.tsx @@ -0,0 +1,128 @@ +import { Alert, Button, ButtonVariant, Tooltip, Wizard, WizardHeader, WizardStep } from '@patternfly/react-core'; +import { Modal, ModalVariant } from '@patternfly/react-core/next'; +import messages from 'locales/messages'; +import React, { useState } from 'react'; +import { useIntl } from 'react-intl'; + +import { styles } from './tagMappingsWizard.styles'; +import { TagMappingsEmptyState } from './tagMappingsWizardEmptyState'; + +interface tagMappingsModalOwnProps { + canWrite?: boolean; + isDisabled?: boolean; +} + +type tagMappingsModalProps = tagMappingsModalOwnProps; + +const TagMappingsWizard: React.FC = ({ canWrite, isDisabled }: tagMappingsModalProps) => { + const [isOpen, setIsOpen] = useState(false); + const [isFinish, setIsFinish] = useState(false); + + // const { goToStepById } = useWizardContext(); + const intl = useIntl(); + + const getActions = () => { + const getTooltip = children => { + if (!canWrite) { + const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); + return {children}; + } + return children; + }; + + return getTooltip( + + ); + }; + + // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. + // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree + + const getSuccessState = () => { + return ( + +
+ +
+ +
+
+
+ ); + }; + + const getWizard = () => { + return ( + + + } + isVisitRequired + onClose={handleOnClose} + > + + Step 1 content + + + Step 2 content + + + + Review step content + + + + ); + }; + + const handleOnClose = () => { + setIsOpen(false); + setIsFinish(false); + }; + + const handleOnClick = () => { + setIsOpen(!isOpen); + }; + + const handleOnCreateTagMapping = () => { + setIsFinish(true); + }; + + const handleOnNavToCreateTagMapping = () => { + setIsFinish(false); + }; + + const handleOnNavToTagMappings = () => { + handleOnClose(); + }; + + return ( + <> + {getActions()} + {isFinish ? getSuccessState() : getWizard()} + + ); +}; + +export default TagMappingsWizard; diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizardEmptyState.tsx b/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizardEmptyState.tsx new file mode 100644 index 000000000..d729ab613 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizardEmptyState.tsx @@ -0,0 +1,69 @@ +import { + Button, + ButtonVariant, + EmptyState, + EmptyStateBody, + EmptyStateFooter, + EmptyStateHeader, + EmptyStateIcon, + EmptyStateVariant, + Icon, +} from '@patternfly/react-core'; +import { CheckCircleIcon } from '@patternfly/react-icons/dist/esm/icons/check-circle-icon'; +import messages from 'locales/messages'; +import React from 'react'; +import { useIntl } from 'react-intl'; + +import { styles } from './tagMappingsWizard.styles'; + +interface TagMappingsEmptyStateOwnProps { + onNavToTagMappings(event); + onNavToCreateTagMapping(event); +} + +type TagMappingsEmptyStateProps = TagMappingsEmptyStateOwnProps; + +const TagMappingsEmptyState: React.FC = ({ + onNavToTagMappings, + onNavToCreateTagMapping, +}: TagMappingsEmptyStateProps) => { + const intl = useIntl(); + + return ( + + + + + } + headingLevel="h5" + /> + + {intl.formatMessage(messages.tagMappingsWizardSuccessDesc, { + learnMore: ( + + {intl.formatMessage(messages.learnMore)} + + ), + warning: {intl.formatMessage(messages.noMappedTagsWarning)}, + })} + + +
+ +
+
+ +
+
+
+ ); +}; + +export { TagMappingsEmptyState }; From ee663c353fdbda1b113cb04da4a768d701b5a589 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Fri, 8 Mar 2024 19:30:03 -0500 Subject: [PATCH 26/61] Added child tags step https://issues.redhat.com/browse/COST-3824 --- locales/data.json | 64 +++++ locales/translations.json | 10 +- src/api/settings.ts | 13 ++ src/locales/messages.ts | 29 ++- .../components/childTags/childTags.tsx | 221 ++++++++++++++++++ .../childTags/childTagsTable.styles.ts | 15 ++ .../components/childTags/childTagsTable.tsx | 128 ++++++++++ .../components/childTags/childTagsToolbar.tsx | 121 ++++++++++ .../tagMappings/components/childTags/index.ts | 1 + .../tagMappings/tagMappingsEmptyState.tsx | 3 +- .../tagMappings/tagMappingsModal/index.ts | 1 + .../tagMappingsModal/tagMappingsModal.tsx | 85 +++++++ .../tagMappings/tagMappingsToolbar.tsx | 6 +- .../tagMappingsWizard/index.ts | 0 .../tagMappingsWizard.styles.ts | 3 + .../tagMappingsWizard/tagMappingsWizard.tsx | 204 ++++++++++++++++ .../tagMappingsWizardEmptyState.tsx | 12 +- .../tagMappingsWizardReview.tsx | 122 ++++++++++ .../tagMappingsWizard/cloudIntegration.tsx | 108 --------- .../tagMappingsWizard/tagMappingsWizard.tsx | 128 ---------- src/routes/settings/tagLabels/tags/tags.tsx | 6 +- .../settings/tagLabels/tags/tagsTable.tsx | 3 +- .../settings/tagLabels/tags/tagsToolbar.tsx | 8 +- src/store/settings/settingsActions.ts | 26 ++- 24 files changed, 1049 insertions(+), 268 deletions(-) create mode 100644 src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx create mode 100644 src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.styles.ts create mode 100644 src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx create mode 100644 src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsToolbar.tsx create mode 100644 src/routes/settings/tagLabels/tagMappings/components/childTags/index.ts create mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappingsModal/index.ts create mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappingsModal/tagMappingsModal.tsx rename src/routes/settings/tagLabels/{ => tagMappings}/tagMappingsWizard/index.ts (100%) rename src/routes/settings/tagLabels/{ => tagMappings}/tagMappingsWizard/tagMappingsWizard.styles.ts (93%) create mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx rename src/routes/settings/tagLabels/{ => tagMappings}/tagMappingsWizard/tagMappingsWizardEmptyState.tsx (87%) create mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx delete mode 100644 src/routes/settings/tagLabels/tagMappingsWizard/cloudIntegration.tsx delete mode 100644 src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.tsx diff --git a/locales/data.json b/locales/data.json index b9864ca2a..d81b5be0c 100644 --- a/locales/data.json +++ b/locales/data.json @@ -2898,6 +2898,14 @@ "value": "Tag names" } ] + }, + "tag_key": { + "value": [ + { + "type": 0, + "value": "Tag keys" + } + ] } }, "type": 5, @@ -7300,6 +7308,14 @@ } ] }, + "tag_key": { + "value": [ + { + "type": 0, + "value": "Filter by tag key" + } + ] + }, "workload": { "value": [ { @@ -7535,6 +7551,14 @@ } ] }, + "tag_key": { + "value": [ + { + "type": 0, + "value": "Tag Key" + } + ] + }, "workload": { "value": [ { @@ -11926,6 +11950,18 @@ "value": "Value" } ], + "tagKeyChild": [ + { + "type": 0, + "value": "Child tag key" + } + ], + "tagKeyParent": [ + { + "type": 0, + "value": "Parent tag key" + } + ], "tagLabels": [ { "type": 0, @@ -11992,6 +12028,28 @@ "value": "Review details" } ], + "tagMappingsWizardReviewDesc": [ + { + "type": 0, + "value": "Review and confirm the tag mappings. Click " + }, + { + "type": 1, + "value": "create" + }, + { + "type": 0, + "value": " to create the mappings, or " + }, + { + "type": 1, + "value": "back" + }, + { + "type": 0, + "value": " to revise. Changes to the reports will be reflected within 24 hours." + } + ], "tagMappingsWizardSelectChildTags": [ { "type": 0, @@ -12353,6 +12411,12 @@ "value": "units" } ], + "unknown": [ + { + "type": 0, + "value": "Unknown" + } + ], "usage": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 09d03bfdd..892c1b5a2 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -213,7 +213,7 @@ "detailsEmptyState": "Processing data to generate a list of all services that sums to a total cost...", "detailsMore": "{value} more...", "detailsMoreClusters": ", {value} more...", - "detailsResourceNames": "{value, select, account {Account names} aws_category {Cost category names} cluster {Cluster names} gcp_project {GCP project names} group {Group} name {Name} node {Node names} org_unit_id {Organizational unit names} payer_tenant_id {Account names} product_service {Service names} project {Project names} region {Region names} resource_location {Region names} service {Service names} service_name {Service names} status {Status} subscription_guid {Account names} source_type {Integration} tag {Tag names} other {}}", + "detailsResourceNames": "{value, select, account {Account names} aws_category {Cost category names} cluster {Cluster names} gcp_project {GCP project names} group {Group} name {Name} node {Node names} org_unit_id {Organizational unit names} payer_tenant_id {Account names} product_service {Service names} project {Project names} region {Region names} resource_location {Region names} service {Service names} service_name {Service names} status {Status} subscription_guid {Account names} source_type {Integration} tag {Tag names} tag_key {Tag keys} other {}}", "detailsSummaryModalTitle": "{groupBy, select, account {{name} accounts} aws_category {{name} cost categories} cluster {{name} clusters} gcp_project {{name} GCP projects} node {{name} nodes} org_unit_id {{name} organizational units} payer_tenant_id {{name} accounts} product_service {{name} services} project {{name} projects} region {{name} regions} resource_location {{name} regions} service {{name} services} service_name {{name} services} subscription_guid {{name} accounts} tag {{name} tags} other {}}", "detailsUnusedCapacityLabel": "Unused capacity", "detailsUnusedRequestsLabel": "Unrequested capacity", @@ -303,12 +303,12 @@ "filterByInputAriaLabel": "{value, select, account {Input for account name} aws_category {Input for cost category name} cluster {Input for cluster name} gcp_project {Input for GCP project name} name {Input for name} node {Input for node name} org_unit_id {Input for organizational unit name} payer_tenant_id {Input for account name} product_service {Input for service_name} project {Input for project name} region {Input for region name} resource_location {Input for region name} service {Input for service name} service_name {Input for service_name} subscription_guid {Input for account name} status {Input for status value} tag {Input for tag name} other {}}", "filterByOrgUnitAriaLabel": "Organizational units", "filterByOrgUnitPlaceholder": "Choose unit", - "filterByPlaceholder": "{value, select, account {Filter by account} aws_category {Filter by cost category} cluster {Filter by cluster} container {Filter by container} description {Filter by description} gcp_project {Filter by GCP project} group {Filter by group} name {Filter by name} node {Filter by node} org_unit_id {Filter by organizational unit} payer_tenant_id {Filter by account} persistent_volume_claim {Filter by persistent volume claim} product_service {Filter by service} project {Filter by project} region {Filter by region} resource_location {Filter by region} service {Filter by service} service_name {Filter by service} source_type {Filter by integration} status {Filter by status} storage_class {Filter by StorageClass} subscription_guid {Filter by account} workload {Filter by workload name} workload_type {Filter by workload type} tag {Filter by tag} other {}}", + "filterByPlaceholder": "{value, select, account {Filter by account} aws_category {Filter by cost category} cluster {Filter by cluster} container {Filter by container} description {Filter by description} gcp_project {Filter by GCP project} group {Filter by group} name {Filter by name} node {Filter by node} org_unit_id {Filter by organizational unit} payer_tenant_id {Filter by account} persistent_volume_claim {Filter by persistent volume claim} product_service {Filter by service} project {Filter by project} region {Filter by region} resource_location {Filter by region} service {Filter by service} service_name {Filter by service} source_type {Filter by integration} status {Filter by status} storage_class {Filter by StorageClass} subscription_guid {Filter by account} workload {Filter by workload name} workload_type {Filter by workload type} tag {Filter by tag} tag_key {Filter by tag key} other {}}", "filterByTagKeyAriaLabel": "Tag keys", "filterByTagValueAriaLabel": "Tag values", "filterByTagValueButtonAriaLabel": "Filter button for tag value", "filterByValuePlaceholder": "Filter by value", - "filterByValues": "{value, select, account {Account} aws_category {Cost category} cluster {Cluster} container {Container} default {Default} gcp_project {GCP project} group {Group} name {Name} node {Node} org_unit_id {Organizational unit} payer_tenant_id {Account} persistent_volume_claim {Persistent volume claim} product_service {Service} project {Project} region {Region} resource_location {Region} service {Service} service_name {Service} source_type {Integration} status {Status} storage_class {StorageClass} subscription_guid {Account} tag {Tag} workload {Workload name} workload_type {Workload type} other {}}", + "filterByValues": "{value, select, account {Account} aws_category {Cost category} cluster {Cluster} container {Container} default {Default} gcp_project {GCP project} group {Group} name {Name} node {Node} org_unit_id {Organizational unit} payer_tenant_id {Account} persistent_volume_claim {Persistent volume claim} product_service {Service} project {Project} region {Region} resource_location {Region} service {Service} service_name {Service} source_type {Integration} status {Status} storage_class {StorageClass} subscription_guid {Account} tag {Tag} tag_key {Tag Key} workload {Workload name} workload_type {Workload type} other {}}", "filterByValuesAriaLabel": "Values", "forDate": "{value} for {dateRange}", "gcp": "Google Cloud Platform", @@ -540,6 +540,8 @@ "tagHeadingKey": "Key", "tagHeadingTitle": "Tags ({value})", "tagHeadingValue": "Value", + "tagKeyChild": "Child tag key", + "tagKeyParent": "Parent tag key", "tagLabels": "Tags and labels", "tagLabelsEnable": "Enable tags and labels", "tagLabelsMap": "Map tags and labels", @@ -549,6 +551,7 @@ "tagMappingsWizardNavToCreateTagMapping": "Create another tag mapping", "tagMappingsWizardNavToTagMappings": "Go back to Cost Management Settings", "tagMappingsWizardReview": "Review details", + "tagMappingsWizardReviewDesc": "Review and confirm the tag mappings. Click {create} to create the mappings, or {back} to revise. Changes to the reports will be reflected within 24 hours.", "tagMappingsWizardSelectChildTags": "Select child tags", "tagMappingsWizardSelectParentTag": "Select parent tag", "tagMappingsWizardSuccess": "Tag mapping successful", @@ -566,6 +569,7 @@ "typeaheadAriaClear": "Clear button and input", "unitTooltips": "{units, select, byte_ms {{value} Byte-ms} core_hours {{value} core-hours} gb {{value} GB} gb_hours {{value} GB-hours} gb_mo {{value} GB-month} gb_ms {{value} GB-ms} gibibyte_month {{value} GiB-month} hour {{value} hours} hrs {{value} hours} ms {{value} milliseconds} vm_hours {{value} VM-hours} other {{value}}}", "units": "{units, select, byte_ms {Byte-ms} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} vm_hours {VM-hours} other {}}", + "unknown": "Unknown", "usage": "Usage", "usageCostDesc": "The portion of cost calculated by applying hourly and/or monthly price list rates to metrics.", "usageCostTitle": "Usage cost", diff --git a/src/api/settings.ts b/src/api/settings.ts index e3ab737ad..4ca8508cc 100644 --- a/src/api/settings.ts +++ b/src/api/settings.ts @@ -10,6 +10,7 @@ export interface SettingsData { uuid?: string; key?: string; enabled?: boolean; + source_type?: string; } export interface PagedMetaDataExt extends PagedMetaData { @@ -26,6 +27,8 @@ export interface Settings { } export interface SettingsPayload { + parent?: string; + children?: string[]; ids?: string[]; } @@ -41,6 +44,11 @@ export const enum SettingsType { tagsEnable = 'tagsEnable', tagsDisable = 'tagsDisable', tagsMappings = 'tagsMappings', + tagsMappingsChild = 'tagsMappingsChild', + tagsMappingsChildAdd = 'tagsMappingsChildAdd', + tagsMappingsChildRemove = 'tagsMappingsChildRemove', + tagsMappingsParent = 'tagsMappingsParent', + tagsMappingsParentRemove = 'tagsMappingsParentRemove', } export const SettingsTypePaths: Partial> = { @@ -54,6 +62,11 @@ export const SettingsTypePaths: Partial> = { [SettingsType.tagsEnable]: 'settings/tags/enable/', [SettingsType.tagsDisable]: 'settings/tags/disable/', [SettingsType.tagsMappings]: 'settings/tags/mappings', + [SettingsType.tagsMappingsChild]: 'settings/tags/mappings/child', + [SettingsType.tagsMappingsChildAdd]: 'settings/tags/mappings/child/add', + [SettingsType.tagsMappingsChildRemove]: 'settings/tags/mappings/child/remove', + [SettingsType.tagsMappingsParent]: 'settings/tags/mappings/parent', + [SettingsType.tagsMappingsParentRemove]: 'settings/tags/mappings/parent/remove', }; export function fetchSettings(settingsType: SettingsType, query: string) { diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 1ef54354f..910880753 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -814,7 +814,7 @@ export default defineMessages({ defaultMessage: 'Review and confirm your cost model configuration and assignments. Click {create} to create the cost model, or {back} to revise.', description: - 'Review and confirm your cost model configuration and assignments. Click {Create} to create the cost model, or {Back} to revise.', + 'Review and confirm your cost model configuration and assignments. Click {create} to create the cost model, or {back} to revise.', id: 'costModelsWizardReviewStatusSubDetails', }, costModelsWizardReviewStatusSubTitle: { @@ -1238,6 +1238,7 @@ export default defineMessages({ 'subscription_guid {Account names} ' + 'source_type {Integration} ' + 'tag {Tag names} ' + + 'tag_key {Tag keys} ' + 'other {}}', description: 'Details table resource names', id: 'detailsResourceNames', @@ -1911,6 +1912,7 @@ export default defineMessages({ 'workload {Filter by workload name} ' + 'workload_type {Filter by workload type} ' + 'tag {Filter by tag} ' + + 'tag_key {Filter by tag key} ' + 'other {}}', description: 'Filter by "value"', id: 'filterByPlaceholder', @@ -1961,6 +1963,7 @@ export default defineMessages({ 'storage_class {StorageClass} ' + 'subscription_guid {Account} ' + 'tag {Tag} ' + + 'tag_key {Tag Key} ' + 'workload {Workload name} ' + 'workload_type {Workload type} ' + 'other {}}', @@ -3196,8 +3199,10 @@ export default defineMessages({ settingsSuccessTags: { defaultMessage: '{value, select, ' + + 'add {{count, plural, one {{count} tag key added} other {{count} tag key added}}} ' + 'enable {{count, plural, one {{count} tag enabled} other {{count} tags enabled}}} ' + 'disable {{count, plural, one {{count} tag disabled} other {{count} tags disabled}}} ' + + 'remove {{count, plural, one {{count} tag key removed} other {{count} tag key removed}}} ' + 'other {}}', description: 'Cost category keys enabled or disabled', id: 'settingsSuccessTags', @@ -3324,6 +3329,16 @@ export default defineMessages({ description: 'Value', id: 'tagHeadingValue', }, + tagKeyChild: { + defaultMessage: 'Child tag key', + description: 'Child tag key', + id: 'tagKeyChild', + }, + tagKeyParent: { + defaultMessage: 'Parent tag key', + description: 'Parent tag key', + id: 'tagKeyParent', + }, tagLabelsEnable: { defaultMessage: 'Enable tags and labels', description: 'Enable tags and labels', @@ -3373,6 +3388,13 @@ export default defineMessages({ description: 'Review details', id: 'tagMappingsWizardReview', }, + tagMappingsWizardReviewDesc: { + defaultMessage: + 'Review and confirm the tag mappings. Click {create} to create the mappings, or {back} to revise. Changes to the reports will be reflected within 24 hours.', + description: + 'Review and confirm the tag mappings. Click {create} to create the mappings, or {back} to revise. Changes to the reports will be reflected within 24 hours.', + id: 'tagMappingsWizardReviewDesc', + }, tagMappingsWizardSelectChildTags: { defaultMessage: 'Select child tags', description: 'Select child tags', @@ -3487,6 +3509,11 @@ export default defineMessages({ description: 'return the proper unit label based on key: "units"', id: 'units', }, + unknown: { + defaultMessage: 'Unknown', + description: 'Unknown', + id: 'unknown', + }, usage: { defaultMessage: 'Usage', description: 'Usage', diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx new file mode 100644 index 000000000..b594ad2a9 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx @@ -0,0 +1,221 @@ +import Unavailable from '@patternfly/react-component-groups/dist/esm/UnavailableContent'; +import { Pagination, PaginationVariant } from '@patternfly/react-core'; +import type { Query } from 'api/queries/query'; +import { getQuery } from 'api/queries/query'; +import type { Settings, SettingsData } from 'api/settings'; +import { SettingsType } from 'api/settings'; +import type { AxiosError } from 'axios'; +import messages from 'locales/messages'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import { LoadingState } from 'routes/components/state/loadingState'; +import * as queryUtils from 'routes/utils/query'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { settingsActions, settingsSelectors } from 'store/settings'; + +import { ChildTagsTable } from './childTagsTable'; +import { styles } from './childTagsTable.styles'; +import { ChildTagsToolbar } from './childTagsToolbar'; + +interface ChildTagsOwnProps { + onBulkSelect(items: SettingsData[]); + onSelect(items: SettingsData[], isSelected: boolean); + selectedItems?: SettingsData[]; +} + +export interface ChildTagsMapProps { + query?: Query; +} + +export interface ChildTagsStateProps { + settings?: Settings; + settingsError?: AxiosError; + settingsStatus?: FetchStatus; + settingsQueryString?: string; +} + +type ChildTagsProps = ChildTagsOwnProps; + +const baseQuery: Query = { + limit: 10, + offset: 0, + filter_by: {}, + order_by: { + parent: 'asc', + }, +}; + +const ChildTags: React.FC = ({ onBulkSelect, onSelect, selectedItems }: ChildTagsProps) => { + const [query, setQuery] = useState({ ...baseQuery }); + const { settings, settingsError, settingsStatus } = useMapToProps({ query }); + const intl = useIntl(); + + const getMappings = () => { + if (settings) { + return settings.data as any; + } + return []; + }; + + const getPagination = (isDisabled = false, isBottom = false) => { + const count = settings?.meta ? settings.meta.count : 0; + const limit = settings?.meta ? settings.meta.limit : baseQuery.limit; + const offset = settings?.meta ? settings.meta.offset : baseQuery.offset; + const page = Math.trunc(offset / limit + 1); + + return ( + handleOnPerPageSelect(perPage)} + onSetPage={(event, pageNumber) => handleOnSetPage(pageNumber)} + page={page} + perPage={limit} + titles={{ + paginationAriaLabel: intl.formatMessage(messages.paginationTitle, { + title: intl.formatMessage(messages.openShift), + placement: isBottom ? 'bottom' : 'top', + }), + }} + variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} + widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + /> + ); + }; + + const getTable = () => { + return ( + handleOnSort(sortType, isSortAscending)} + selectedItems={selectedItems} + settings={settings} + /> + ); + }; + + const getToolbar = (mappings: SettingsData[]) => { + const itemsTotal = settings?.meta ? settings.meta.count : 0; + + return ( + handleOnFilterAdded(filter)} + onFilterRemoved={filter => handleOnFilterRemoved(filter)} + pagination={getPagination(isDisabled)} + selectedItems={selectedItems} + query={query} + /> + ); + }; + + const handleOnBulkSelect = (action: string) => { + if (action === 'none') { + onBulkSelect([]); + } else if (action === 'page') { + const newSelectedItems = [...selectedItems]; + getMappings().map(val => { + if (!newSelectedItems.find(item => item.uuid === val.uuid)) { + newSelectedItems.push(val); + } + }); + onBulkSelect(newSelectedItems); + } + }; + + const handleOnFilterAdded = filter => { + const newQuery = queryUtils.handleOnFilterAdded(query, filter); + setQuery(newQuery); + }; + + const handleOnFilterRemoved = filter => { + const newQuery = queryUtils.handleOnFilterRemoved(query, filter); + setQuery(newQuery); + }; + + const handleOnPerPageSelect = perPage => { + const newQuery = queryUtils.handleOnPerPageSelect(query, perPage, true); + setQuery(newQuery); + }; + + const handleOnSetPage = pageNumber => { + const newQuery = queryUtils.handleOnSetPage(query, settings, pageNumber, true); + setQuery(newQuery); + }; + + const handleOnSort = (sortType, isSortAscending) => { + const newQuery = queryUtils.handleOnSort(query, sortType, isSortAscending); + setQuery(newQuery); + }; + + if (settingsError) { + return ; + } + + const mappings = getMappings(); + const isDisabled = mappings.length === 0; + + return ( + <> + {getToolbar(mappings)} + {settingsStatus === FetchStatus.inProgress ? ( +
+ +
+ ) : ( + <> + {getTable()} +
{getPagination(isDisabled, true)}
+ + )} + + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = ({ query }: ChildTagsMapProps): ChildTagsStateProps => { + const dispatch: ThunkDispatch = useDispatch(); + + const settingsQuery = { + filter_by: query.filter_by, + limit: query.limit, + offset: query.offset, + order_by: query.order_by, + }; + const settingsQueryString = getQuery(settingsQuery); + const settings = useSelector((state: RootState) => + settingsSelectors.selectSettings(state, SettingsType.tagsMappingsChild, settingsQueryString) + ); + const settingsStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsStatus(state, SettingsType.tagsMappingsChild, settingsQueryString) + ); + const settingsError = useSelector((state: RootState) => + settingsSelectors.selectSettingsError(state, SettingsType.tagsMappingsChild, settingsQueryString) + ); + + useEffect(() => { + if (!settingsError && settingsStatus !== FetchStatus.inProgress) { + dispatch(settingsActions.fetchSettings(SettingsType.tagsMappingsChild, settingsQueryString)); + } + }, [query]); + + return { + settings, + settingsError, + settingsStatus, + settingsQueryString, + }; +}; + +export default ChildTags; diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.styles.ts b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.styles.ts new file mode 100644 index 000000000..95218da16 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.styles.ts @@ -0,0 +1,15 @@ +import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; +import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; +import type React from 'react'; + +export const styles = { + loading: { + backgroundColor: global_BackgroundColor_light_100.value, + minHeight: '520px', + }, + pagination: { + backgroundColor: global_BackgroundColor_light_100.value, + paddingBottom: global_spacer_md.value, + paddingTop: global_spacer_md.value, + }, +} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx new file mode 100644 index 000000000..3815eff63 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx @@ -0,0 +1,128 @@ +import 'routes/components/dataTable/dataTable.scss'; + +import type { Settings } from 'api/settings'; +import type { SettingsData } from 'api/settings'; +import messages from 'locales/messages'; +import React from 'react'; +import type { WrappedComponentProps } from 'react-intl'; +import { injectIntl } from 'react-intl'; +import { DataTable } from 'routes/components/dataTable'; +import type { RouterComponentProps } from 'utils/router'; +import { withRouter } from 'utils/router'; + +interface ChildTagsTableOwnProps extends RouterComponentProps, WrappedComponentProps { + filterBy?: any; + isAllSelected?: boolean; + isLoading?: boolean; + onSelect(items: SettingsData[], isSelected: boolean); + onSort(value: string, isSortAscending: boolean); + orderBy?: any; + selectedItems?: SettingsData[]; + settings: Settings; +} + +interface ChildTagsTableState { + columns?: any[]; + rows?: any[]; +} + +type ChildTagsTableProps = ChildTagsTableOwnProps; + +class ChildTagsTableBase extends React.Component { + public state: ChildTagsTableState = { + columns: [], + rows: [], + }; + + public componentDidMount() { + this.initDatum(); + } + + public componentDidUpdate(prevProps: ChildTagsTableProps) { + const { selectedItems, settings } = this.props; + const currentReport = settings?.data ? JSON.stringify(settings.data) : ''; + const previousReport = prevProps?.settings.data ? JSON.stringify(prevProps.settings.data) : ''; + + if (previousReport !== currentReport || prevProps.selectedItems !== selectedItems) { + this.initDatum(); + } + } + + private initDatum = () => { + const { intl, selectedItems, settings } = this.props; + if (!settings) { + return; + } + + const rows = []; + const tags = settings?.data ? (settings.data as any) : []; + + const columns = [ + // Sorting with tag keys is not supported + { + name: '', + }, + { + orderBy: 'key', + name: intl.formatMessage(messages.detailsResourceNames, { value: 'tag_key' }), + ...(tags.length && { isSortable: true }), + }, + { + orderBy: 'source_type', + name: intl.formatMessage(messages.sourceType), + ...(tags.length && { isSortable: true }), + }, + ]; + + tags.map(item => { + rows.push({ + cells: [ + { + name: '', + }, + { + value: item.key ? item.key : '', + }, + { + value: intl.formatMessage(messages.sourceTypes, { value: item?.source_type?.toLowerCase() }), + }, + ], + item, + selected: selectedItems && selectedItems.find(val => val.uuid === item.uuid) !== undefined, + }); + }); + + const filteredColumns = (columns as any[]).filter(column => !column.hidden); + const filteredRows = rows.map(({ ...row }) => { + row.cells = row.cells.filter(cell => !cell.hidden); + return row; + }); + + this.setState({ + columns: filteredColumns, + rows: filteredRows, + }); + }; + + public render() { + const { filterBy, isLoading, onSelect, onSort, orderBy } = this.props; + const { columns, rows } = this.state; + + return ( + + ); + } +} + +const ChildTagsTable = injectIntl(withRouter(ChildTagsTableBase)); + +export { ChildTagsTable }; diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsToolbar.tsx b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsToolbar.tsx new file mode 100644 index 000000000..d383cfa4c --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsToolbar.tsx @@ -0,0 +1,121 @@ +import type { Query } from 'api/queries/query'; +import { ResourcePathsType } from 'api/resources/resource'; +import type { SettingsData } from 'api/settings'; +import messages from 'locales/messages'; +import React from 'react'; +import type { WrappedComponentProps } from 'react-intl'; +import { injectIntl } from 'react-intl'; +import { connect } from 'react-redux'; +import { BasicToolbar } from 'routes/components/dataToolbar'; +import type { ToolbarChipGroupExt } from 'routes/components/dataToolbar/utils/common'; +import type { Filter } from 'routes/utils/filter'; +import { createMapStateToProps } from 'store/common'; + +interface ChildTagsToolbarOwnProps { + enabledTagsCount?: number; + enabledTagsLimit?: number; + isAllSelected?: boolean; + isDisabled?: boolean; + isPrimaryActionDisabled?: boolean; + isSecondaryActionDisabled?: boolean; + itemsPerPage?: number; + itemsTotal?: number; + onBulkSelect(action: string); + onFilterAdded(filter: Filter); + onFilterRemoved(filter: Filter); + pagination?: React.ReactNode; + selectedItems?: SettingsData[]; + query?: Query; +} + +interface ChildTagsToolbarStateProps { + // TBD... +} + +interface ChildTagsToolbarDispatchProps { + // TBD... +} + +interface ChildTagsToolbarState { + categoryOptions?: ToolbarChipGroupExt[]; +} + +type ChildTagsToolbarProps = ChildTagsToolbarOwnProps & + ChildTagsToolbarStateProps & + ChildTagsToolbarDispatchProps & + WrappedComponentProps; + +class ChildTagsToolbarBase extends React.Component { + protected defaultState: ChildTagsToolbarState = {}; + public state: ChildTagsToolbarState = { ...this.defaultState }; + + public componentDidMount() { + this.setState({ + categoryOptions: this.getCategoryOptions(), + }); + } + + private getCategoryOptions = (): ToolbarChipGroupExt[] => { + const { intl } = this.props; + + const options = [ + { + ariaLabelKey: 'tag_key', + placeholderKey: 'tag_key', + key: 'key', + name: intl.formatMessage(messages.filterByValues, { value: 'tag_key' }), + }, + ]; + return options; + }; + + public render() { + const { + isAllSelected, + isDisabled, + itemsPerPage, + itemsTotal, + onBulkSelect, + onFilterAdded, + onFilterRemoved, + pagination, + query, + } = this.props; + const { categoryOptions } = this.state; + + return ( + + ); + } +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const mapStateToProps = createMapStateToProps(() => { + return { + // TBD... + }; +}); + +const mapDispatchToProps: ChildTagsToolbarDispatchProps = { + // TBD... +}; + +const ChildTagsToolbarConnect = connect(mapStateToProps, mapDispatchToProps)(ChildTagsToolbarBase); +const ChildTagsToolbar = injectIntl(ChildTagsToolbarConnect); + +export { ChildTagsToolbar }; diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/index.ts b/src/routes/settings/tagLabels/tagMappings/components/childTags/index.ts new file mode 100644 index 000000000..96a8105cf --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/components/childTags/index.ts @@ -0,0 +1 @@ +export { default as ChildTags } from './childTags'; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx index 367bd3c3e..2796a3f56 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx @@ -11,8 +11,7 @@ import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circ import messages from 'locales/messages'; import React from 'react'; import { useIntl } from 'react-intl'; - -import { TagMappingsWizard } from '../tagMappingsWizard'; +import { TagMappingsWizard } from 'routes/settings/tagLabels/tagMappings/tagMappingsWizard'; interface TagMappingsEmptyStateOwnProps { canWrite?: boolean; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/index.ts b/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/index.ts new file mode 100644 index 000000000..0d97b0765 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/index.ts @@ -0,0 +1 @@ +export { default as TagMappingsModal } from './tagMappingsModal'; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/tagMappingsModal.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/tagMappingsModal.tsx new file mode 100644 index 000000000..201aaf188 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/tagMappingsModal.tsx @@ -0,0 +1,85 @@ +import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; +import { Modal, ModalBody, ModalHeader, ModalVariant } from '@patternfly/react-core/next'; +import type { SettingsData } from 'api/settings'; +import messages from 'locales/messages'; +import React, { useState } from 'react'; +import { useIntl } from 'react-intl'; +import { ChildTags } from 'routes/settings/tagLabels/tagMappings/components/childTags'; +import { useStateCallback } from 'utils/hooks'; + +interface tagMappingsModalOwnProps { + canWrite?: boolean; + isDisabled?: boolean; +} + +type tagMappingsModalProps = tagMappingsModalOwnProps; + +const TagMappingsModal: React.FC = ({ canWrite, isDisabled }: tagMappingsModalProps) => { + const [isOpen, setIsOpen] = useState(false); + const [selectedItems, setSelectedItems] = useStateCallback([]); + + const intl = useIntl(); + + const getActions = () => { + const getTooltip = children => { + if (!canWrite) { + const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); + return {children}; + } + return children; + }; + + return getTooltip( + + ); + }; + + const handleOnBulkSelect = (items: SettingsData[]) => { + setSelectedItems(items); + }; + + const handleOnClose = () => { + setIsOpen(false); + }; + + const handleOnClick = () => { + setIsOpen(!isOpen); + }; + + const handleOnSelect = (items: SettingsData[], isSelected: boolean = false) => { + let newItems = [...selectedItems]; + if (items && items.length > 0) { + if (isSelected) { + items.map(item => newItems.push(item)); + } else { + items.map(item => { + newItems = newItems.filter(val => val.uuid !== item.uuid); + }); + } + } + setSelectedItems(newItems); + }; + + // const handleOnCreateTagMapping = () => { + // // TBD... + // }; + + // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. + // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree + + return ( + <> + {getActions()} + + + + + + + + ); +}; + +export default TagMappingsModal; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx index 8ebf68f76..1573f0f90 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx @@ -7,11 +7,10 @@ import { injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import { BasicToolbar } from 'routes/components/dataToolbar'; import type { ToolbarChipGroupExt } from 'routes/components/dataToolbar/utils/common'; +import { TagMappingsWizard } from 'routes/settings/tagLabels/tagMappings/tagMappingsWizard'; import type { Filter } from 'routes/utils/filter'; import { createMapStateToProps } from 'store/common'; -import { TagMappingsWizard } from '../tagMappingsWizard'; - interface TagMappingsToolbarOwnProps { canWrite?: boolean; enabledTagsCount?: number; @@ -45,7 +44,7 @@ type TagMappingsToolbarProps = TagMappingsToolbarOwnProps & TagMappingsToolbarDispatchProps & WrappedComponentProps; -export class TagMappingsToolbarBase extends React.Component { +class TagMappingsToolbarBase extends React.Component { protected defaultState: TagMappingsToolbarState = {}; public state: TagMappingsToolbarState = { ...this.defaultState }; @@ -165,4 +164,3 @@ const TagMappingsToolbarConnect = connect(mapStateToProps, mapDispatchToProps)(T const TagMappingsToolbar = injectIntl(TagMappingsToolbarConnect); export { TagMappingsToolbar }; -export type { TagMappingsToolbarProps }; diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/index.ts b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/index.ts similarity index 100% rename from src/routes/settings/tagLabels/tagMappingsWizard/index.ts rename to src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/index.ts diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.styles.ts b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.styles.ts similarity index 93% rename from src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.styles.ts rename to src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.styles.ts index 094bcdffa..c969cc33e 100644 --- a/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.styles.ts +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.styles.ts @@ -16,6 +16,9 @@ export const styles = { backgroundColor: global_BackgroundColor_light_100.value, minHeight: '520px', }, + reviewTable: { + marginTop: '-10px', + }, spacing: { marginRight: global_FontSize_md.value, }, diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx new file mode 100644 index 000000000..ed0a8c914 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx @@ -0,0 +1,204 @@ +import { Button, ButtonVariant, Tooltip, Wizard, WizardHeader, WizardStep } from '@patternfly/react-core'; +import { Modal, ModalVariant } from '@patternfly/react-core/next'; +import type { SettingsData } from 'api/settings'; +import { SettingsType } from 'api/settings'; +import type { AxiosError } from 'axios'; +import messages from 'locales/messages'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import { ChildTags } from 'routes/settings/tagLabels/tagMappings/components/childTags'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { settingsActions, settingsSelectors } from 'store/settings'; + +import { styles } from './tagMappingsWizard.styles'; +import { TagMappingsEmptyState } from './tagMappingsWizardEmptyState'; +import { TagMappingsWizardReview } from './tagMappingsWizardReview'; + +interface TagMappingsWizardOwnProps { + canWrite?: boolean; + isDisabled?: boolean; +} + +interface TagMappingsWizardStateProps { + settingsError?: AxiosError; + settingsStatus?: FetchStatus; +} + +type TagMappingsWizardProps = TagMappingsWizardOwnProps; + +const TagMappingsWizard: React.FC = ({ canWrite, isDisabled }: TagMappingsWizardProps) => { + const [isOpen, setIsOpen] = useState(false); + const [isFinished, setIsFinished] = useState(false); + const [childTags, setChildTags] = useState([]); + const [parentTag] = useState('test'); + + const dispatch: ThunkDispatch = useDispatch(); + const intl = useIntl(); + + const { settingsError, settingsStatus } = useMapToProps(); + + const getActions = () => { + const getTooltip = children => { + if (!canWrite) { + const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); + return {children}; + } + return children; + }; + + return getTooltip( + + ); + }; + + const getSuccessEmptyState = () => { + return ( + +
+ +
+ +
+
+
+ ); + }; + + const handleOnBulkSelect = (items: SettingsData[]) => { + setChildTags(items); + }; + + const handleOnSelect = (items: SettingsData[], isSelected: boolean = false) => { + let newItems = [...childTags]; + if (items && items.length > 0) { + if (isSelected) { + items.map(item => newItems.push(item)); + } else { + items.map(item => { + newItems = newItems.filter(val => val.uuid !== item.uuid); + }); + } + } + setChildTags(newItems); + }; + + // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. + // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree + + const getWizard = () => { + return ( + + + } + isVisitRequired + onClose={handleOnClose} + > + + + + + Step 2 content + + + + + + + ); + }; + + const handleOnClose = () => { + setIsOpen(false); + setIsFinished(false); + }; + + const handleOnClick = () => { + setIsOpen(!isOpen); + }; + + const handleOnCreateTagMapping = () => { + if (settingsStatus !== FetchStatus.inProgress) { + dispatch( + settingsActions.updateSettings(SettingsType.tagsMappingsChildAdd, { + parent: parentTag, + children: childTags.map(item => item.uuid), + }) + ); + } + }; + + const handleOnReset = () => { + setIsFinished(false); + }; + + useEffect(() => { + if (settingsStatus === FetchStatus.complete && !settingsError) { + setIsFinished(true); + } + }, [settingsError, settingsStatus]); + + return ( + <> + {getActions()} + {isFinished ? getSuccessEmptyState() : getWizard()} + + ); +}; + +const useMapToProps = (): TagMappingsWizardStateProps => { + const settingsStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildAdd) + ); + const settingsError = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateError(state, SettingsType.tagsMappingsChildAdd) + ); + + return { + settingsError, + settingsStatus, + }; +}; + +export default TagMappingsWizard; diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizardEmptyState.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardEmptyState.tsx similarity index 87% rename from src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizardEmptyState.tsx rename to src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardEmptyState.tsx index d729ab613..bc43c5807 100644 --- a/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizardEmptyState.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardEmptyState.tsx @@ -17,15 +17,15 @@ import { useIntl } from 'react-intl'; import { styles } from './tagMappingsWizard.styles'; interface TagMappingsEmptyStateOwnProps { - onNavToTagMappings(event); - onNavToCreateTagMapping(event); + onClose(event); + onReset(event); } type TagMappingsEmptyStateProps = TagMappingsEmptyStateOwnProps; const TagMappingsEmptyState: React.FC = ({ - onNavToTagMappings, - onNavToCreateTagMapping, + onClose, + onReset, }: TagMappingsEmptyStateProps) => { const intl = useIntl(); @@ -52,12 +52,12 @@ const TagMappingsEmptyState: React.FC = ({
-
-
diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx new file mode 100644 index 000000000..89f4afc7c --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx @@ -0,0 +1,122 @@ +import { + Alert, + Stack, + StackItem, + Text, + TextContent, + TextList, + TextListItem, + TextListItemVariants, + TextListVariants, + Title, + TitleSizes, +} from '@patternfly/react-core'; +import { Table, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; +import type { SettingsData } from 'api/settings'; +import type { AxiosError } from 'axios'; +import messages from 'locales/messages'; +import React from 'react'; +import { useIntl } from 'react-intl'; +import { FetchStatus } from 'store/common'; + +import { styles } from './tagMappingsWizard.styles'; + +interface tagMappingsWizardReviewOwnProps { + childTags?: SettingsData[]; + parentTag?: string; + settingsError?: AxiosError; + settingsStatus?: FetchStatus; +} + +type tagMappingsWizardReviewProps = tagMappingsWizardReviewOwnProps; + +const TagMappingsWizardReview: React.FC = ({ + childTags = [], + parentTag = '', + settingsError, + settingsStatus, +}: tagMappingsWizardReviewProps) => { + const intl = useIntl(); + + const parseApiError = error => { + if (error.response && error.response.data) { + if (error.response.data.Error) { + return error.response.data.Error; + } + if (error.response.data.errors) { + return error.response.data.errors.map(er => `${er.source}: ${er.detail}`).join(', '); + } + } else if (error.message) { + return error.message; + } + return 'unknown'; + }; + + return ( + <> + {settingsStatus !== FetchStatus.inProgress && settingsError && ( + + )} + + + + {intl.formatMessage(messages.tagMappingsWizardReview)} + + + + + + {intl.formatMessage(messages.tagMappingsWizardReviewDesc, { + create: {intl.formatMessage(messages.create)}, + back: {intl.formatMessage(messages.back)}, + })} + + + + + + + + {intl.formatMessage(messages.tagKeyChild)} + + +
+ + + + + + + + + {childTags.map((item, index) => ( + + + + + ))} + +
+ {intl.formatMessage(messages.detailsResourceNames, { value: 'tag_key' })} + {intl.formatMessage(messages.sourceType)}
{item.key} + {intl.formatMessage(messages.sourceTypes, { value: item?.source_type?.toLowerCase() })} +
+
+
+ + {intl.formatMessage(messages.tagKeyParent)} + + {parentTag} +
+
+
+
+ + ); +}; + +export { TagMappingsWizardReview }; diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/cloudIntegration.tsx b/src/routes/settings/tagLabels/tagMappingsWizard/cloudIntegration.tsx deleted file mode 100644 index 446a3a874..000000000 --- a/src/routes/settings/tagLabels/tagMappingsWizard/cloudIntegration.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { Text, TextList, TextListItem, TextVariants } from '@patternfly/react-core'; -import type { Provider } from 'api/providers'; -import { ProviderType } from 'api/providers'; -import type { AxiosError } from 'axios'; -import messages from 'locales/messages'; -import React, { useEffect } from 'react'; -import { useIntl } from 'react-intl'; -import { useDispatch, useSelector } from 'react-redux'; -import type { AnyAction } from 'redux'; -import type { ThunkDispatch } from 'redux-thunk'; -import { routes } from 'routes'; -import { NotAvailable } from 'routes/components/page/notAvailable'; -import { LoadingState } from 'routes/components/state/loadingState'; -import type { RootState } from 'store'; -import { FetchStatus } from 'store/common'; -import { providersActions, providersSelectors } from 'store/providers'; -import { formatPath, getReleasePath } from 'utils/paths'; - -import { styles } from './tagMappingsWizard.styles'; - -interface CloudIntegrationOwnProps { - uuid?: string; -} - -export interface CloudIntegrationStateProps { - provider: Provider; - providerError: AxiosError; - providerFetchStatus: FetchStatus; - providerQueryString: string; - uuid?: string; -} - -export interface CloudIntegrationMapProps { - // TBD... -} - -type CloudIntegrationProps = CloudIntegrationOwnProps; - -const CloudIntegration: React.FC = ({ uuid }: CloudIntegrationProps) => { - const intl = useIntl(); - - const { provider, providerError, providerFetchStatus } = useMapToProps({ uuid }); - - const title = intl.formatMessage(messages.optimizations); - - if (providerError) { - return ; - } - - const release = getReleasePath(); - - if (providerFetchStatus === FetchStatus.inProgress) { - return ( -
- -
- ); - } - return ( - <> - {intl.formatMessage(messages.cloudIntegration)} - - - - {intl.formatMessage(messages.source, { value: provider?.source_type?.toLowerCase() })} - - {provider?.name} - - {provider?.cost_models?.length === 0 && ( - - {intl.formatMessage(messages.assignCostModel)} - - )} - - - ); -}; - -// eslint-disable-next-line no-empty-pattern -const useMapToProps = ({ uuid }): CloudIntegrationStateProps => { - const dispatch: ThunkDispatch = useDispatch(); - - const providerQueryString = uuid; - const provider = useSelector( - (state: RootState) => providersSelectors.selectProviders(state, ProviderType.uuid, providerQueryString) as Provider - ); - const providerError = useSelector((state: RootState) => - providersSelectors.selectProvidersError(state, ProviderType.uuid, providerQueryString) - ); - const providerFetchStatus = useSelector((state: RootState) => - providersSelectors.selectProvidersFetchStatus(state, ProviderType.uuid, providerQueryString) - ); - - useEffect(() => { - if (!providerError && providerFetchStatus !== FetchStatus.inProgress) { - dispatch(providersActions.fetchProviders(ProviderType.uuid, providerQueryString)); - } - }, []); - - return { - provider, - providerError, - providerFetchStatus, - providerQueryString, - }; -}; - -export { CloudIntegration }; diff --git a/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.tsx b/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.tsx deleted file mode 100644 index f61ba4d14..000000000 --- a/src/routes/settings/tagLabels/tagMappingsWizard/tagMappingsWizard.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { Alert, Button, ButtonVariant, Tooltip, Wizard, WizardHeader, WizardStep } from '@patternfly/react-core'; -import { Modal, ModalVariant } from '@patternfly/react-core/next'; -import messages from 'locales/messages'; -import React, { useState } from 'react'; -import { useIntl } from 'react-intl'; - -import { styles } from './tagMappingsWizard.styles'; -import { TagMappingsEmptyState } from './tagMappingsWizardEmptyState'; - -interface tagMappingsModalOwnProps { - canWrite?: boolean; - isDisabled?: boolean; -} - -type tagMappingsModalProps = tagMappingsModalOwnProps; - -const TagMappingsWizard: React.FC = ({ canWrite, isDisabled }: tagMappingsModalProps) => { - const [isOpen, setIsOpen] = useState(false); - const [isFinish, setIsFinish] = useState(false); - - // const { goToStepById } = useWizardContext(); - const intl = useIntl(); - - const getActions = () => { - const getTooltip = children => { - if (!canWrite) { - const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); - return {children}; - } - return children; - }; - - return getTooltip( - - ); - }; - - // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. - // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree - - const getSuccessState = () => { - return ( - -
- -
- -
-
-
- ); - }; - - const getWizard = () => { - return ( - - - } - isVisitRequired - onClose={handleOnClose} - > - - Step 1 content - - - Step 2 content - - - - Review step content - - - - ); - }; - - const handleOnClose = () => { - setIsOpen(false); - setIsFinish(false); - }; - - const handleOnClick = () => { - setIsOpen(!isOpen); - }; - - const handleOnCreateTagMapping = () => { - setIsFinish(true); - }; - - const handleOnNavToCreateTagMapping = () => { - setIsFinish(false); - }; - - const handleOnNavToTagMappings = () => { - handleOnClose(); - }; - - return ( - <> - {getActions()} - {isFinish ? getSuccessState() : getWizard()} - - ); -}; - -export default TagMappingsWizard; diff --git a/src/routes/settings/tagLabels/tags/tags.tsx b/src/routes/settings/tagLabels/tags/tags.tsx index 2c5edeca2..3af2d0826 100644 --- a/src/routes/settings/tagLabels/tags/tags.tsx +++ b/src/routes/settings/tagLabels/tags/tags.tsx @@ -30,7 +30,7 @@ export interface TagsMapProps { query?: Query; } -export interface TagsStateProps { +interface TagsStateProps { settings?: Settings; settingsError?: AxiosError; settingsStatus?: FetchStatus; @@ -97,7 +97,7 @@ const Tags: React.FC = ({ canWrite }) => { filterBy={query.filter_by} isLoading={settingsStatus === FetchStatus.inProgress} orderBy={query.order_by} - onSelect={handleonSelect} + onSelect={handleOnSelect} onSort={(sortType, isSortAscending) => handleOnSort(sortType, isSortAscending)} settings={settings} selectedItems={selectedItems} @@ -193,7 +193,7 @@ const Tags: React.FC = ({ canWrite }) => { setQuery(newQuery); }; - const handleonSelect = (items: SettingsData[], isSelected: boolean = false) => { + const handleOnSelect = (items: SettingsData[], isSelected: boolean = false) => { let newItems = [...selectedItems]; if (items && items.length > 0) { if (isSelected) { diff --git a/src/routes/settings/tagLabels/tags/tagsTable.tsx b/src/routes/settings/tagLabels/tags/tagsTable.tsx index 8a65d4b55..859f2abe5 100644 --- a/src/routes/settings/tagLabels/tags/tagsTable.tsx +++ b/src/routes/settings/tagLabels/tags/tagsTable.tsx @@ -7,7 +7,6 @@ import React from 'react'; import type { WrappedComponentProps } from 'react-intl'; import { injectIntl } from 'react-intl'; import { DataTable } from 'routes/components/dataTable'; -import type { ComputedReportItem } from 'routes/utils/computedReport/getComputedReportItems'; import type { RouterComponentProps } from 'utils/router'; import { withRouter } from 'utils/router'; @@ -16,7 +15,7 @@ interface TagsTableOwnProps extends RouterComponentProps, WrappedComponentProps filterBy?: any; isAllSelected?: boolean; isLoading?: boolean; - onSelect(items: ComputedReportItem[], isSelected: boolean); + onSelect(items: SettingsData[], isSelected: boolean); onSort(value: string, isSortAscending: boolean); orderBy?: any; selectedItems?: SettingsData[]; diff --git a/src/routes/settings/tagLabels/tags/tagsToolbar.tsx b/src/routes/settings/tagLabels/tags/tagsToolbar.tsx index 5b5b5e6a9..28cbb1efe 100644 --- a/src/routes/settings/tagLabels/tags/tagsToolbar.tsx +++ b/src/routes/settings/tagLabels/tags/tagsToolbar.tsx @@ -1,6 +1,7 @@ import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; -import type { OcpQuery } from 'api/queries/ocpQuery'; +import type { Query } from 'api/queries/query'; import { ResourcePathsType } from 'api/resources/resource'; +import type { SettingsData } from 'api/settings'; import messages from 'locales/messages'; import React from 'react'; import type { WrappedComponentProps } from 'react-intl'; @@ -8,7 +9,6 @@ import { injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import { BasicToolbar } from 'routes/components/dataToolbar'; import type { ToolbarChipGroupExt } from 'routes/components/dataToolbar/utils/common'; -import type { ComputedReportItem } from 'routes/utils/computedReport/getComputedReportItems'; import type { Filter } from 'routes/utils/filter'; import { createMapStateToProps } from 'store/common'; @@ -30,8 +30,8 @@ interface TagsToolbarOwnProps { onFilterAdded(filter: Filter); onFilterRemoved(filter: Filter); pagination?: React.ReactNode; - query?: OcpQuery; - selectedItems?: ComputedReportItem[]; + query?: Query; + selectedItems?: SettingsData[]; showBulkSelectAll?: boolean; } diff --git a/src/store/settings/settingsActions.ts b/src/store/settings/settingsActions.ts index e5cd7da85..caf9f5a09 100644 --- a/src/store/settings/settingsActions.ts +++ b/src/store/settings/settingsActions.ts @@ -21,15 +21,12 @@ export const fetchSettingsRequest = createAction('settings/fetch/request')(); export const fetchSettingsFailure = createAction('settings/fetch/failure')(); -export const updateSettingsRequest = createAction('settings/awsCategoryKeys/update/request')(); -export const updateSettingsSuccess = createAction('settings/awsCategoryKeys/update/success')< +export const updateSettingsRequest = createAction('settings/update/request')(); +export const updateSettingsSuccess = createAction('settings/update/success')< AxiosResponse, SettingsActionMeta >(); -export const updateSettingsFailure = createAction('settings/awsCategoryKeys/update/failure')< - AxiosError, - SettingsActionMeta ->(); +export const updateSettingsFailure = createAction('settings/update/failure')(); export function fetchSettings(settingsType: SettingsType, settingsQueryString: string): ThunkAction { return (dispatch, getState) => { @@ -97,6 +94,15 @@ export function updateSettings(settingsType: SettingsType, payload: SettingsPayl msg = messages.settingsSuccessTags; status = 'enable'; break; + case SettingsType.tagsMappingsChildAdd: + msg = messages.settingsSuccessTags; + status = 'add'; + break; + case SettingsType.tagsMappingsChildRemove: + case SettingsType.tagsMappingsParentRemove: + msg = messages.settingsSuccessTags; + status = 'remove'; + break; } return apiUpdateSettings(settingsType, payload) @@ -105,7 +111,13 @@ export function updateSettings(settingsType: SettingsType, payload: SettingsPayl dispatch( addNotification({ title: intl.formatMessage(msg, { - count: payload.ids.length, + count: payload.ids + ? payload.ids.length + : payload.children + ? payload.children.length + : payload.parent + ? payload.parent.length + : 0, value: status, }), description: intl.formatMessage(messages.settingsSuccessChanges), From 9ce308b817ab7cdd1c257d66210ad73d7cdc627b Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Fri, 8 Mar 2024 21:47:54 -0500 Subject: [PATCH 27/61] Added parent tags step --- src/routes/components/dataTable/dataTable.tsx | 4 +- .../components/childTags/childTagsTable.tsx | 2 +- .../components/childTags/childTagsToolbar.tsx | 35 ++- .../components/parentTags/index.ts | 1 + .../components/parentTags/parentTags.tsx | 205 ++++++++++++++++++ .../parentTags/parentTagsTable.styles.ts | 15 ++ .../components/parentTags/parentTagsTable.tsx | 129 +++++++++++ .../parentTags/parentTagsToolbar.tsx | 147 +++++++++++++ .../tagLabels/tagMappings/tagMappings.tsx | 7 +- .../tagMappings/tagMappingsEmptyState.tsx | 4 +- .../tagMappings/tagMappingsToolbar.tsx | 6 +- .../tagMappingsWizard/tagMappingsWizard.tsx | 79 ++++--- .../tagMappingsWizardReview.tsx | 10 +- .../settings/tagLabels/tags/tagsToolbar.tsx | 2 - 14 files changed, 604 insertions(+), 42 deletions(-) create mode 100644 src/routes/settings/tagLabels/tagMappings/components/parentTags/index.ts create mode 100644 src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTags.tsx create mode 100644 src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.styles.ts create mode 100644 src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx create mode 100644 src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsToolbar.tsx diff --git a/src/routes/components/dataTable/dataTable.tsx b/src/routes/components/dataTable/dataTable.tsx index 09d11a9fb..99b629e8a 100644 --- a/src/routes/components/dataTable/dataTable.tsx +++ b/src/routes/components/dataTable/dataTable.tsx @@ -34,6 +34,7 @@ interface DataTableOwnProps { orderBy: any; rows?: any[]; selectedItems?: ComputedReportItem[]; + variant?: 'checkbox' | 'radio'; } type DataTableProps = DataTableOwnProps & RouterComponentProps & WrappedComponentProps; @@ -119,7 +120,7 @@ class DataTable extends React.Component { }; public render() { - const { columns, intl, isActionsCell = false, isLoading, isSelectable, rows } = this.props; + const { columns, intl, isActionsCell = false, isLoading, isSelectable, rows, variant } = this.props; return ( <> @@ -168,6 +169,7 @@ class DataTable extends React.Component { isSelected: row.selected, onSelect: (_evt, isSelected) => this.handleOnSelect(isSelected, rowIndex), rowIndex, + variant, }} style={item.style} /> diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx index 3815eff63..07a129d70 100644 --- a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx +++ b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx @@ -68,7 +68,7 @@ class ChildTagsTableBase extends React.Component = ({ onSelect, selectedItems }: ParentTagsProps) => { + const [query, setQuery] = useState({ ...baseQuery }); + const { settings, settingsError, settingsStatus } = useMapToProps({ query }); + const intl = useIntl(); + + const getMappings = () => { + if (settings) { + return settings.data as any; + } + return []; + }; + + const getPagination = (isDisabled = false, isBottom = false) => { + const count = settings?.meta ? settings.meta.count : 0; + const limit = settings?.meta ? settings.meta.limit : baseQuery.limit; + const offset = settings?.meta ? settings.meta.offset : baseQuery.offset; + const page = Math.trunc(offset / limit + 1); + + return ( + handleOnPerPageSelect(perPage)} + onSetPage={(event, pageNumber) => handleOnSetPage(pageNumber)} + page={page} + perPage={limit} + titles={{ + paginationAriaLabel: intl.formatMessage(messages.paginationTitle, { + title: intl.formatMessage(messages.openShift), + placement: isBottom ? 'bottom' : 'top', + }), + }} + variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} + widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + /> + ); + }; + + const getTable = () => { + return ( + handleOnSort(sortType, isSortAscending)} + selectedItems={selectedItems} + settings={settings} + /> + ); + }; + + const getToolbar = (mappings: SettingsData[]) => { + const itemsTotal = settings?.meta ? settings.meta.count : 0; + + return ( + handleOnFilterAdded(filter)} + onFilterRemoved={filter => handleOnFilterRemoved(filter)} + pagination={getPagination(isDisabled)} + selectedItems={selectedItems} + query={query} + /> + ); + }; + + const handleOnFilterAdded = filter => { + const newQuery = queryUtils.handleOnFilterAdded(query, filter); + setQuery(newQuery); + }; + + const handleOnFilterRemoved = filter => { + const newQuery = queryUtils.handleOnFilterRemoved(query, filter); + setQuery(newQuery); + }; + + const handleOnPerPageSelect = perPage => { + const newQuery = queryUtils.handleOnPerPageSelect(query, perPage, true); + setQuery(newQuery); + }; + + const handleOnSetPage = pageNumber => { + const newQuery = queryUtils.handleOnSetPage(query, settings, pageNumber, true); + setQuery(newQuery); + }; + + const handleOnSort = (sortType, isSortAscending) => { + const newQuery = queryUtils.handleOnSort(query, sortType, isSortAscending); + setQuery(newQuery); + }; + + if (settingsError) { + return ; + } + + const mappings = getMappings(); + const isDisabled = mappings.length === 0; + + return ( + <> + {getToolbar(mappings)} + {settingsStatus === FetchStatus.inProgress ? ( +
+ +
+ ) : ( + <> + {getTable()} +
{getPagination(isDisabled, true)}
+ + )} + + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = ({ query }: ParentTagsMapProps): ParentTagsStateProps => { + const dispatch: ThunkDispatch = useDispatch(); + + const settingsQuery = { + filter_by: query.filter_by, + limit: query.limit, + offset: query.offset, + order_by: query.order_by, + }; + const settingsQueryString = getQuery(settingsQuery); + const settings = useSelector((state: RootState) => + settingsSelectors.selectSettings(state, SettingsType.tagsMappingsParent, settingsQueryString) + ); + const settingsStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsStatus(state, SettingsType.tagsMappingsParent, settingsQueryString) + ); + const settingsError = useSelector((state: RootState) => + settingsSelectors.selectSettingsError(state, SettingsType.tagsMappingsParent, settingsQueryString) + ); + + useEffect(() => { + if (!settingsError && settingsStatus !== FetchStatus.inProgress) { + dispatch(settingsActions.fetchSettings(SettingsType.tagsMappingsParent, settingsQueryString)); + } + }, [query]); + + return { + settings, + settingsError, + settingsStatus, + settingsQueryString, + }; +}; + +export default ParentTags; diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.styles.ts b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.styles.ts new file mode 100644 index 000000000..95218da16 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.styles.ts @@ -0,0 +1,15 @@ +import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; +import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; +import type React from 'react'; + +export const styles = { + loading: { + backgroundColor: global_BackgroundColor_light_100.value, + minHeight: '520px', + }, + pagination: { + backgroundColor: global_BackgroundColor_light_100.value, + paddingBottom: global_spacer_md.value, + paddingTop: global_spacer_md.value, + }, +} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx new file mode 100644 index 000000000..f428bcfe2 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx @@ -0,0 +1,129 @@ +import 'routes/components/dataTable/dataTable.scss'; + +import type { Settings } from 'api/settings'; +import type { SettingsData } from 'api/settings'; +import messages from 'locales/messages'; +import React from 'react'; +import type { WrappedComponentProps } from 'react-intl'; +import { injectIntl } from 'react-intl'; +import { DataTable } from 'routes/components/dataTable'; +import type { RouterComponentProps } from 'utils/router'; +import { withRouter } from 'utils/router'; + +interface ParentTagsTableOwnProps extends RouterComponentProps, WrappedComponentProps { + filterBy?: any; + isAllSelected?: boolean; + isLoading?: boolean; + onSelect(items: SettingsData[], isSelected: boolean); + onSort(value: string, isSortAscending: boolean); + orderBy?: any; + selectedItems?: SettingsData[]; + settings: Settings; +} + +interface ParentTagsTableState { + columns?: any[]; + rows?: any[]; +} + +type ParentTagsTableProps = ParentTagsTableOwnProps; + +class ParentTagsTableBase extends React.Component { + public state: ParentTagsTableState = { + columns: [], + rows: [], + }; + + public componentDidMount() { + this.initDatum(); + } + + public componentDidUpdate(prevProps: ParentTagsTableProps) { + const { selectedItems, settings } = this.props; + const currentReport = settings?.data ? JSON.stringify(settings.data) : ''; + const previousReport = prevProps?.settings.data ? JSON.stringify(prevProps.settings.data) : ''; + + if (previousReport !== currentReport || prevProps.selectedItems !== selectedItems) { + this.initDatum(); + } + } + + private initDatum = () => { + const { intl, selectedItems, settings } = this.props; + if (!settings) { + return; + } + + const rows = []; + const tags = settings?.data ? (settings.data as any) : []; + + const columns = [ + // Sorting with tag keys is not supported + { + name: '', + }, + { + orderBy: 'key', + name: intl.formatMessage(messages.detailsResourceNames, { value: 'tag_key' }), + ...(tags.length && { isSortable: true }), + }, + { + orderBy: 'provider_type', // Todo: Rename as source_type? + name: intl.formatMessage(messages.sourceType), + ...(tags.length && { isSortable: true }), + }, + ]; + + tags.map(item => { + rows.push({ + cells: [ + { + name: '', + }, + { + value: item.key ? item.key : '', + }, + { + value: intl.formatMessage(messages.sourceTypes, { value: item?.source_type?.toLowerCase() }), + }, + ], + item, + selected: selectedItems && selectedItems.find(val => val.uuid === item.uuid) !== undefined, + }); + }); + + const filteredColumns = (columns as any[]).filter(column => !column.hidden); + const filteredRows = rows.map(({ ...row }) => { + row.cells = row.cells.filter(cell => !cell.hidden); + return row; + }); + + this.setState({ + columns: filteredColumns, + rows: filteredRows, + }); + }; + + public render() { + const { filterBy, isLoading, onSelect, onSort, orderBy } = this.props; + const { columns, rows } = this.state; + + return ( + + ); + } +} + +const ParentTagsTable = injectIntl(withRouter(ParentTagsTableBase)); + +export { ParentTagsTable }; diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsToolbar.tsx b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsToolbar.tsx new file mode 100644 index 000000000..0572a1096 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsToolbar.tsx @@ -0,0 +1,147 @@ +import type { Query } from 'api/queries/query'; +import type { SettingsData } from 'api/settings'; +import messages from 'locales/messages'; +import React from 'react'; +import type { WrappedComponentProps } from 'react-intl'; +import { injectIntl } from 'react-intl'; +import { connect } from 'react-redux'; +import { BasicToolbar } from 'routes/components/dataToolbar'; +import type { ToolbarChipGroupExt } from 'routes/components/dataToolbar/utils/common'; +import type { Filter } from 'routes/utils/filter'; +import { createMapStateToProps } from 'store/common'; + +interface ParentTagsToolbarOwnProps { + enabledTagsCount?: number; + enabledTagsLimit?: number; + isAllSelected?: boolean; + isDisabled?: boolean; + isPrimaryActionDisabled?: boolean; + isSecondaryActionDisabled?: boolean; + itemsPerPage?: number; + itemsTotal?: number; + onFilterAdded(filter: Filter); + onFilterRemoved(filter: Filter); + pagination?: React.ReactNode; + selectedItems?: SettingsData[]; + query?: Query; +} + +interface ParentTagsToolbarStateProps { + // TBD... +} + +interface ParentTagsToolbarDispatchProps { + // TBD... +} + +interface ParentTagsToolbarState { + categoryOptions?: ToolbarChipGroupExt[]; +} + +type ParentTagsToolbarProps = ParentTagsToolbarOwnProps & + ParentTagsToolbarStateProps & + ParentTagsToolbarDispatchProps & + WrappedComponentProps; + +class ParentTagsToolbarBase extends React.Component { + protected defaultState: ParentTagsToolbarState = {}; + public state: ParentTagsToolbarState = { ...this.defaultState }; + + public componentDidMount() { + this.setState({ + categoryOptions: this.getCategoryOptions(), + }); + } + + private getCategoryOptions = (): ToolbarChipGroupExt[] => { + const { intl } = this.props; + + const options = [ + { + ariaLabelKey: 'tag_key', + placeholderKey: 'tag_key', + key: 'key', + name: intl.formatMessage(messages.filterByValues, { value: 'tag_key' }), + }, + { + key: 'source_type', + name: intl.formatMessage(messages.filterByValues, { value: 'source_type' }), + selectClassName: 'selectOverride', // A selector from routes/components/dataToolbar/dataToolbar.scss + selectOptions: [ + { + key: 'AWS', + name: intl.formatMessage(messages.aws), + }, + { + key: 'Azure', + name: intl.formatMessage(messages.azure), + }, + { + key: 'GCP', + name: intl.formatMessage(messages.gcp), + }, + // { + // key: 'IBM', + // name: intl.formatMessage(messages.ibm), // Todo: enable when supported by API + // }, + { + key: 'OCI', + name: intl.formatMessage(messages.oci), + }, + { + key: 'OCP', + name: intl.formatMessage(messages.openShift), + }, + ], + }, + ]; + return options; + }; + + public render() { + const { + isAllSelected, + isDisabled, + itemsPerPage, + itemsTotal, + onFilterAdded, + onFilterRemoved, + pagination, + query, + selectedItems, + } = this.props; + const { categoryOptions } = this.state; + + return ( + + ); + } +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const mapStateToProps = createMapStateToProps(() => { + return { + // TBD... + }; +}); + +const mapDispatchToProps: ParentTagsToolbarDispatchProps = { + // TBD... +}; + +const ParentTagsToolbarConnect = connect(mapStateToProps, mapDispatchToProps)(ParentTagsToolbarBase); +const ParentTagsToolbar = injectIntl(ParentTagsToolbarConnect); + +export { ParentTagsToolbar }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx index be7376e5b..55f37206e 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx @@ -112,6 +112,7 @@ const TagMappings: React.FC = ({ canWrite }) => { itemsTotal={itemsTotal} onFilterAdded={filter => handleOnFilterAdded(filter)} onFilterRemoved={filter => handleOnFilterRemoved(filter)} + onWizardClose={handleOnWizardClose} pagination={getPagination(isDisabled)} query={query} /> @@ -143,6 +144,10 @@ const TagMappings: React.FC = ({ canWrite }) => { setQuery(newQuery); }; + const handleOnWizardClose = () => { + setQuery({ ...query }); // Force refresh + }; + if (settingsError) { return ; } @@ -173,7 +178,7 @@ const TagMappings: React.FC = ({ canWrite }) => { ) : (
- +
)} diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx index 2796a3f56..00cd5ac50 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx @@ -16,6 +16,7 @@ import { TagMappingsWizard } from 'routes/settings/tagLabels/tagMappings/tagMapp interface TagMappingsEmptyStateOwnProps { canWrite?: boolean; isDisabled?: boolean; + onWizardClose(); } type TagMappingsEmptyStateProps = TagMappingsEmptyStateOwnProps; @@ -23,6 +24,7 @@ type TagMappingsEmptyStateProps = TagMappingsEmptyStateOwnProps; const TagMappingsEmptyState: React.FC = ({ canWrite, isDisabled, + onWizardClose, }: TagMappingsEmptyStateOwnProps) => { const intl = useIntl(); @@ -45,7 +47,7 @@ const TagMappingsEmptyState: React.FC = ({ - +
diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx index 1573f0f90..fff973eed 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx @@ -1,5 +1,4 @@ import type { OcpQuery } from 'api/queries/ocpQuery'; -import { ResourcePathsType } from 'api/resources/resource'; import messages from 'locales/messages'; import React from 'react'; import type { WrappedComponentProps } from 'react-intl'; @@ -23,6 +22,7 @@ interface TagMappingsToolbarOwnProps { itemsTotal?: number; onFilterAdded(filter: Filter); onFilterRemoved(filter: Filter); + onWizardClose(); pagination?: React.ReactNode; query?: OcpQuery; } @@ -124,6 +124,7 @@ class TagMappingsToolbarBase extends React.Component} + actions={} categoryOptions={categoryOptions} isAllSelected={isAllSelected} isDisabled={isDisabled} @@ -142,7 +143,6 @@ class TagMappingsToolbarBase extends React.Component ); diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx index ed0a8c914..4337a42d8 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx @@ -10,6 +10,7 @@ import { useDispatch, useSelector } from 'react-redux'; import type { AnyAction } from 'redux'; import type { ThunkDispatch } from 'redux-thunk'; import { ChildTags } from 'routes/settings/tagLabels/tagMappings/components/childTags'; +import { ParentTags } from 'routes/settings/tagLabels/tagMappings/components/parentTags'; import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; import { settingsActions, settingsSelectors } from 'store/settings'; @@ -21,6 +22,7 @@ import { TagMappingsWizardReview } from './tagMappingsWizardReview'; interface TagMappingsWizardOwnProps { canWrite?: boolean; isDisabled?: boolean; + onClose(); } interface TagMappingsWizardStateProps { @@ -30,11 +32,15 @@ interface TagMappingsWizardStateProps { type TagMappingsWizardProps = TagMappingsWizardOwnProps; -const TagMappingsWizard: React.FC = ({ canWrite, isDisabled }: TagMappingsWizardProps) => { +const TagMappingsWizard: React.FC = ({ + canWrite, + isDisabled, + onClose, +}: TagMappingsWizardProps) => { const [isOpen, setIsOpen] = useState(false); const [isFinished, setIsFinished] = useState(false); const [childTags, setChildTags] = useState([]); - const [parentTag] = useState('test'); + const [parentTags, setParentTags] = useState([]); const dispatch: ThunkDispatch = useDispatch(); const intl = useIntl(); @@ -74,24 +80,6 @@ const TagMappingsWizard: React.FC = ({ canWrite, isDisab ); }; - const handleOnBulkSelect = (items: SettingsData[]) => { - setChildTags(items); - }; - - const handleOnSelect = (items: SettingsData[], isSelected: boolean = false) => { - let newItems = [...childTags]; - if (items && items.length > 0) { - if (isSelected) { - items.map(item => newItems.push(item)); - } else { - items.map(item => { - newItems = newItems.filter(val => val.uuid !== item.uuid); - }); - } - } - setChildTags(newItems); - }; - // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree @@ -116,17 +104,17 @@ const TagMappingsWizard: React.FC = ({ canWrite, isDisab id="step-1" name={intl.formatMessage(messages.tagMappingsWizardSelectChildTags)} > - + - Step 2 content + = ({ canWrite, isDisab > @@ -149,20 +137,27 @@ const TagMappingsWizard: React.FC = ({ canWrite, isDisab ); }; - const handleOnClose = () => { - setIsOpen(false); - setIsFinished(false); + const handleOnBulkSelect = (items: SettingsData[]) => { + setChildTags(items); }; const handleOnClick = () => { setIsOpen(!isOpen); }; + const handleOnClose = () => { + setIsOpen(false); + setIsFinished(false); + if (onClose) { + onClose(); + } + }; + const handleOnCreateTagMapping = () => { if (settingsStatus !== FetchStatus.inProgress) { dispatch( settingsActions.updateSettings(SettingsType.tagsMappingsChildAdd, { - parent: parentTag, + parent: parentTags.length ? parentTags[0].uuid : undefined, children: childTags.map(item => item.uuid), }) ); @@ -173,6 +168,34 @@ const TagMappingsWizard: React.FC = ({ canWrite, isDisab setIsFinished(false); }; + const handleOnSelectChild = (items: SettingsData[], isSelected: boolean = false) => { + let newItems = [...childTags]; + if (items && items.length > 0) { + if (isSelected) { + items.map(item => newItems.push(item)); + } else { + items.map(item => { + newItems = newItems.filter(val => val.uuid !== item.uuid); + }); + } + } + setChildTags(newItems); + }; + + const handleOnSelectParent = (items: SettingsData[], isSelected: boolean = false) => { + let newItems = []; + if (items && items.length > 0) { + if (isSelected) { + items.map(item => newItems.push(item)); + } else { + items.map(item => { + newItems = newItems.filter(val => val.uuid !== item.uuid); + }); + } + } + setParentTags(newItems); + }; + useEffect(() => { if (settingsStatus === FetchStatus.complete && !settingsError) { setIsFinished(true); diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx index 89f4afc7c..ded950b5f 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx @@ -23,7 +23,7 @@ import { styles } from './tagMappingsWizard.styles'; interface tagMappingsWizardReviewOwnProps { childTags?: SettingsData[]; - parentTag?: string; + parentTags?: SettingsData[]; settingsError?: AxiosError; settingsStatus?: FetchStatus; } @@ -32,7 +32,7 @@ type tagMappingsWizardReviewProps = tagMappingsWizardReviewOwnProps; const TagMappingsWizardReview: React.FC = ({ childTags = [], - parentTag = '', + parentTags = [], settingsError, settingsStatus, }: tagMappingsWizardReviewProps) => { @@ -110,7 +110,11 @@ const TagMappingsWizardReview: React.FC = ({ {intl.formatMessage(messages.tagKeyParent)} - {parentTag} + {parentTags.map((val, index) => ( + + {val.key} + + ))} diff --git a/src/routes/settings/tagLabels/tags/tagsToolbar.tsx b/src/routes/settings/tagLabels/tags/tagsToolbar.tsx index 28cbb1efe..6c55fb746 100644 --- a/src/routes/settings/tagLabels/tags/tagsToolbar.tsx +++ b/src/routes/settings/tagLabels/tags/tagsToolbar.tsx @@ -1,6 +1,5 @@ import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; import type { Query } from 'api/queries/query'; -import { ResourcePathsType } from 'api/resources/resource'; import type { SettingsData } from 'api/settings'; import messages from 'locales/messages'; import React from 'react'; @@ -200,7 +199,6 @@ export class TagsToolbarBase extends React.Component Date: Sat, 9 Mar 2024 22:43:59 -0500 Subject: [PATCH 28/61] Added expandable table for tag mappings https://issues.redhat.com/browse/COST-3824 --- locales/data.json | 128 +++++++++++ locales/translations.json | 8 +- src/locales/messages.ts | 7 + .../components/dataTable/dataTable.styles.ts | 3 + src/routes/components/dataTable/dataTable.tsx | 8 +- .../components/dataTable/expandableTable.tsx | 213 ++++++++++++++++++ src/routes/components/dataTable/index.ts | 1 + .../emptyFilterState/emptyFilterState.tsx | 13 +- .../tagMappings/tagMappings.styles.ts | 4 + .../tagLabels/tagMappings/tagMappings.tsx | 48 +++- .../tagMappings/tagMappingsTable.tsx | 84 +++++-- .../tagMappings/tagMappingsToolbar.tsx | 34 +-- .../tagMappingsWizard/tagMappingsWizard.tsx | 35 +-- src/store/settings/settingsActions.ts | 2 + src/store/settings/settingsReducer.ts | 9 +- 15 files changed, 522 insertions(+), 75 deletions(-) create mode 100644 src/routes/components/dataTable/expandableTable.tsx diff --git a/locales/data.json b/locales/data.json index d81b5be0c..895b8c82f 100644 --- a/locales/data.json +++ b/locales/data.json @@ -7100,6 +7100,30 @@ "value": "Input for tag name" } ] + }, + "tag_key": { + "value": [ + { + "type": 0, + "value": "Input for tag key" + } + ] + }, + "tag_key_child": { + "value": [ + { + "type": 0, + "value": "Input for child tag key" + } + ] + }, + "tag_key_parent": { + "value": [ + { + "type": 0, + "value": "Input for parent tag key" + } + ] } }, "type": 5, @@ -7316,6 +7340,22 @@ } ] }, + "tag_key_child": { + "value": [ + { + "type": 0, + "value": "Filter by child tag key" + } + ] + }, + "tag_key_parent": { + "value": [ + { + "type": 0, + "value": "Filter by parent tag key" + } + ] + }, "workload": { "value": [ { @@ -7559,6 +7599,22 @@ } ] }, + "tag_key_child": { + "value": [ + { + "type": 0, + "value": "Child tag Key" + } + ] + }, + "tag_key_parent": { + "value": [ + { + "type": 0, + "value": "Parent tag Key" + } + ] + }, "workload": { "value": [ { @@ -11581,6 +11637,42 @@ "settingsSuccessTags": [ { "options": { + "add": { + "value": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": " tag key added" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": " tag key added" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ] + }, "disable": { "value": [ { @@ -11655,6 +11747,42 @@ }, "other": { "value": [] + }, + "remove": { + "value": [ + { + "offset": 0, + "options": { + "one": { + "value": [ + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": " tag key removed" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": " tag key removed" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ] } }, "type": 5, diff --git a/locales/translations.json b/locales/translations.json index 892c1b5a2..e1720dd5a 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -300,15 +300,15 @@ "filterByCostCategoryKeyAriaLabel": "Cost category keys", "filterByCostCategoryValueAriaLabel": "Cost category values", "filterByCostCategoryValueButtonAriaLabel": "Filter button for cost category value", - "filterByInputAriaLabel": "{value, select, account {Input for account name} aws_category {Input for cost category name} cluster {Input for cluster name} gcp_project {Input for GCP project name} name {Input for name} node {Input for node name} org_unit_id {Input for organizational unit name} payer_tenant_id {Input for account name} product_service {Input for service_name} project {Input for project name} region {Input for region name} resource_location {Input for region name} service {Input for service name} service_name {Input for service_name} subscription_guid {Input for account name} status {Input for status value} tag {Input for tag name} other {}}", + "filterByInputAriaLabel": "{value, select, account {Input for account name} aws_category {Input for cost category name} cluster {Input for cluster name} gcp_project {Input for GCP project name} name {Input for name} node {Input for node name} org_unit_id {Input for organizational unit name} payer_tenant_id {Input for account name} product_service {Input for service_name} project {Input for project name} region {Input for region name} resource_location {Input for region name} service {Input for service name} service_name {Input for service_name} subscription_guid {Input for account name} status {Input for status value} tag {Input for tag name} tag_key {Input for tag key} tag_key_child {Input for child tag key} tag_key_parent {Input for parent tag key} other {}}", "filterByOrgUnitAriaLabel": "Organizational units", "filterByOrgUnitPlaceholder": "Choose unit", - "filterByPlaceholder": "{value, select, account {Filter by account} aws_category {Filter by cost category} cluster {Filter by cluster} container {Filter by container} description {Filter by description} gcp_project {Filter by GCP project} group {Filter by group} name {Filter by name} node {Filter by node} org_unit_id {Filter by organizational unit} payer_tenant_id {Filter by account} persistent_volume_claim {Filter by persistent volume claim} product_service {Filter by service} project {Filter by project} region {Filter by region} resource_location {Filter by region} service {Filter by service} service_name {Filter by service} source_type {Filter by integration} status {Filter by status} storage_class {Filter by StorageClass} subscription_guid {Filter by account} workload {Filter by workload name} workload_type {Filter by workload type} tag {Filter by tag} tag_key {Filter by tag key} other {}}", + "filterByPlaceholder": "{value, select, account {Filter by account} aws_category {Filter by cost category} cluster {Filter by cluster} container {Filter by container} description {Filter by description} gcp_project {Filter by GCP project} group {Filter by group} name {Filter by name} node {Filter by node} org_unit_id {Filter by organizational unit} payer_tenant_id {Filter by account} persistent_volume_claim {Filter by persistent volume claim} product_service {Filter by service} project {Filter by project} region {Filter by region} resource_location {Filter by region} service {Filter by service} service_name {Filter by service} source_type {Filter by integration} status {Filter by status} storage_class {Filter by StorageClass} subscription_guid {Filter by account} workload {Filter by workload name} workload_type {Filter by workload type} tag {Filter by tag} tag_key {Filter by tag key} tag_key_child {Filter by child tag key} tag_key_parent {Filter by parent tag key} other {}}", "filterByTagKeyAriaLabel": "Tag keys", "filterByTagValueAriaLabel": "Tag values", "filterByTagValueButtonAriaLabel": "Filter button for tag value", "filterByValuePlaceholder": "Filter by value", - "filterByValues": "{value, select, account {Account} aws_category {Cost category} cluster {Cluster} container {Container} default {Default} gcp_project {GCP project} group {Group} name {Name} node {Node} org_unit_id {Organizational unit} payer_tenant_id {Account} persistent_volume_claim {Persistent volume claim} product_service {Service} project {Project} region {Region} resource_location {Region} service {Service} service_name {Service} source_type {Integration} status {Status} storage_class {StorageClass} subscription_guid {Account} tag {Tag} tag_key {Tag Key} workload {Workload name} workload_type {Workload type} other {}}", + "filterByValues": "{value, select, account {Account} aws_category {Cost category} cluster {Cluster} container {Container} default {Default} gcp_project {GCP project} group {Group} name {Name} node {Node} org_unit_id {Organizational unit} payer_tenant_id {Account} persistent_volume_claim {Persistent volume claim} product_service {Service} project {Project} region {Region} resource_location {Region} service {Service} service_name {Service} source_type {Integration} status {Status} storage_class {StorageClass} subscription_guid {Account} tag {Tag} tag_key {Tag Key} tag_key_child {Child tag Key} tag_key_parent {Parent tag Key} workload {Workload name} workload_type {Workload type} other {}}", "filterByValuesAriaLabel": "Values", "forDate": "{value} for {dateRange}", "gcp": "Google Cloud Platform", @@ -518,7 +518,7 @@ "settingsSuccessCostCategories": "{value, select, enable {{count, plural, one {{count} cost category key enabled} other {{count} cost category keys enabled}}} disable {{count, plural, one {{count} cost category key disabled} other {{count} cost category keys disabled}}} other {}}", "settingsSuccessDesc": "Settings for Cost Management were replaced with new values", "settingsSuccessPlatformProjects": "{value, select, add {{count, plural, one {{count} projects added to Platform projects} other {{count} project added to Platform projects}}} remove {{count, plural, one {{count} projects removed from Platform projects} other {{count} project removed from Platform projects}}} other {}}", - "settingsSuccessTags": "{value, select, enable {{count, plural, one {{count} tag enabled} other {{count} tags enabled}}} disable {{count, plural, one {{count} tag disabled} other {{count} tags disabled}}} other {}}", + "settingsSuccessTags": "{value, select, add {{count, plural, one {{count} tag key added} other {{count} tag key added}}} enable {{count, plural, one {{count} tag enabled} other {{count} tags enabled}}} disable {{count, plural, one {{count} tag disabled} other {{count} tags disabled}}} remove {{count, plural, one {{count} tag key removed} other {{count} tag key removed}}} other {}}", "settingsSuccessTitle": "Application settings saved", "settingsTagsErrorDesc": "You currently have {value} tags enabled", "settingsTagsErrorTitle": "You can not enable more than {value} tags total", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 910880753..911e4db68 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -1870,6 +1870,9 @@ export default defineMessages({ 'subscription_guid {Input for account name} ' + 'status {Input for status value} ' + 'tag {Input for tag name} ' + + 'tag_key {Input for tag key} ' + + 'tag_key_child {Input for child tag key} ' + + 'tag_key_parent {Input for parent tag key} ' + 'other {}}', description: 'Input for {value} name', id: 'filterByInputAriaLabel', @@ -1913,6 +1916,8 @@ export default defineMessages({ 'workload_type {Filter by workload type} ' + 'tag {Filter by tag} ' + 'tag_key {Filter by tag key} ' + + 'tag_key_child {Filter by child tag key} ' + + 'tag_key_parent {Filter by parent tag key} ' + 'other {}}', description: 'Filter by "value"', id: 'filterByPlaceholder', @@ -1964,6 +1969,8 @@ export default defineMessages({ 'subscription_guid {Account} ' + 'tag {Tag} ' + 'tag_key {Tag Key} ' + + 'tag_key_child {Child tag Key} ' + + 'tag_key_parent {Parent tag Key} ' + 'workload {Workload name} ' + 'workload_type {Workload type} ' + 'other {}}', diff --git a/src/routes/components/dataTable/dataTable.styles.ts b/src/routes/components/dataTable/dataTable.styles.ts index 9519faaaf..c18c613e3 100644 --- a/src/routes/components/dataTable/dataTable.styles.ts +++ b/src/routes/components/dataTable/dataTable.styles.ts @@ -24,6 +24,9 @@ export const styles = { height: '35vh', width: '100%', }, + expandableRowBorder: { + border: 0, + }, infoArrow: { position: 'relative', }, diff --git a/src/routes/components/dataTable/dataTable.tsx b/src/routes/components/dataTable/dataTable.tsx index 99b629e8a..af3fe975f 100644 --- a/src/routes/components/dataTable/dataTable.tsx +++ b/src/routes/components/dataTable/dataTable.tsx @@ -40,12 +40,6 @@ interface DataTableOwnProps { type DataTableProps = DataTableOwnProps & RouterComponentProps & WrappedComponentProps; class DataTable extends React.Component { - constructor(props: DataTableProps) { - super(props); - this.handleOnSelect = this.handleOnSelect.bind(this); - this.handleOnSort = this.handleOnSort.bind(this); - } - private getEmptyState = () => { const { emptyState, filterBy, intl } = this.props; @@ -120,7 +114,7 @@ class DataTable extends React.Component { }; public render() { - const { columns, intl, isActionsCell = false, isLoading, isSelectable, rows, variant } = this.props; + const { columns, intl, isActionsCell, isLoading, isSelectable, rows, variant } = this.props; return ( <> diff --git a/src/routes/components/dataTable/expandableTable.tsx b/src/routes/components/dataTable/expandableTable.tsx new file mode 100644 index 000000000..47af223c6 --- /dev/null +++ b/src/routes/components/dataTable/expandableTable.tsx @@ -0,0 +1,213 @@ +import './dataTable.scss'; + +import { + Bullseye, + EmptyState, + EmptyStateBody, + EmptyStateHeader, + EmptyStateIcon, + Spinner, +} from '@patternfly/react-core'; +import { CalculatorIcon } from '@patternfly/react-icons/dist/esm/icons/calculator-icon'; +import type { ThProps } from '@patternfly/react-table'; +import { SortByDirection, Table, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; +import messages from 'locales/messages'; +import React from 'react'; +import type { WrappedComponentProps } from 'react-intl'; +import { injectIntl } from 'react-intl'; +import { EmptyFilterState } from 'routes/components/state/emptyFilterState'; +import type { ComputedReportItem } from 'routes/utils/computedReport/getComputedReportItems'; +import type { RouterComponentProps } from 'utils/router'; +import { withRouter } from 'utils/router'; + +import { styles } from './dataTable.styles'; + +interface ExpandableTableOwnProps { + columns?: any[]; + emptyState?: React.ReactNode; + filterBy: any; + isActionsCell?: boolean; + isAllExpanded?: boolean; + isLoading?: boolean; + onSort(value: string, isSortAscending: boolean); + orderBy: any; + rows?: any[]; + selectedItems?: ComputedReportItem[]; +} + +interface ExpandableTableState { + expandedRows?: Set; + rows?: any[]; +} + +type ExpandableTableProps = ExpandableTableOwnProps & RouterComponentProps & WrappedComponentProps; + +class ExpandableTable extends React.Component { + public state: ExpandableTableState = { + expandedRows: new Set(), + }; + + private getEmptyState = () => { + const { emptyState, filterBy, intl } = this.props; + + if (filterBy) { + for (const val of Object.values(filterBy)) { + if (val !== '*') { + return ; + } + } + } + return emptyState ? ( + emptyState + ) : ( + + } /> + {intl.formatMessage(messages.detailsEmptyState)} + + ); + }; + + private getSortBy = index => { + const { columns, orderBy } = this.props; + + const direction = orderBy && orderBy[columns[index].orderBy]; + + return direction + ? { + index, + direction, + } + : {}; + }; + + private getSortParams = (index: number): ThProps['sort'] => { + return { + sortBy: this.getSortBy(index), + onSort: (_evt, i, direction) => this.handleOnSort(i, direction), + columnIndex: index, + }; + }; + + private handleOnSort = (index, direction) => { + const { columns, onSort } = this.props; + + if (onSort) { + const orderBy = columns[index].orderBy; + const isSortAscending = direction === SortByDirection.asc; + onSort(orderBy, isSortAscending); + } + }; + + private handleOnToggle = (item: any) => { + const { expandedRows } = this.state; + + if (expandedRows.has(item)) { + expandedRows.delete(item); + } else { + expandedRows.add(item); + } + this.setState({ expandedRows }); + }; + + public render() { + const { columns, intl, isActionsCell, isAllExpanded, isLoading, rows } = this.props; + const { expandedRows } = this.state; + + return ( + <> + + + + {columns.map((col, index) => ( + + ))} + + + + {isLoading ? ( + + + + ) : ( + rows.map((row, rowIndex) => { + const isExpanded = isAllExpanded || expandedRows.has(row?.item); + return ( + + + {row.cells.map((item, cellIndex) => + cellIndex === 0 ? ( + + ) + )} + + {row?.children.map((child, childIndex) => ( + + {child.cells.map((item, cellIndex) => + cellIndex === 0 ? ( + + ) + )} + + ))} + + ); + }) + )} + +
+ {col.name} +
+ +
+ +
+
+
this.handleOnToggle(row?.item), + }} + key={`cell-${cellIndex}-${rowIndex}`} + /> + ) : ( + + {item.value} +
+ ) : ( + + {item.value} +
+ {rows.length === 0 &&
{this.getEmptyState()}
} + + ); + } +} + +export default injectIntl(withRouter(ExpandableTable)); diff --git a/src/routes/components/dataTable/index.ts b/src/routes/components/dataTable/index.ts index afd6ba4cd..139a68465 100644 --- a/src/routes/components/dataTable/index.ts +++ b/src/routes/components/dataTable/index.ts @@ -1,2 +1,3 @@ export { default as DataTable } from './dataTable'; +export { default as ExpandableTable } from './expandableTable'; export { default as SelectableTable } from './selectableTable'; diff --git a/src/routes/components/state/emptyFilterState/emptyFilterState.tsx b/src/routes/components/state/emptyFilterState/emptyFilterState.tsx index 7370aec93..04a61cb87 100644 --- a/src/routes/components/state/emptyFilterState/emptyFilterState.tsx +++ b/src/routes/components/state/emptyFilterState/emptyFilterState.tsx @@ -57,7 +57,18 @@ const EmptyFilterStateBase: React.FC = ({ let showEmptyState1 = false; let showEmptyState2 = false; - if (filter && filter.length && !Array.isArray(filter)) { + if (filter && Array.isArray(filter)) { + for (const val of filter) { + if (isFilter1(val)) { + showEmptyState1 = true; + break; + } + if (isFilter2(val)) { + showEmptyState2 = true; + break; + } + } + } else if (filter && !Array.isArray(filter)) { for (const val of filter.split(',')) { if (isFilter1(val)) { showEmptyState1 = true; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts b/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts index 01ecb7545..ed8cbcab5 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts +++ b/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts @@ -1,5 +1,6 @@ import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; +import global_spacer_sm from '@patternfly/react-tokens/dist/js/global_spacer_sm'; import type React from 'react'; export const styles = { @@ -9,6 +10,9 @@ export const styles = { emptyStateContainer: { paddingTop: global_spacer_md.value, }, + expandableRowContent: { + paddingLeft: global_spacer_sm.value, + }, pagination: { backgroundColor: global_BackgroundColor_light_100.value, paddingBottom: global_spacer_md.value, diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx index 55f37206e..24d295453 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx @@ -35,6 +35,8 @@ export interface MappingsStateProps { settingsError?: AxiosError; settingsStatus?: FetchStatus; settingsQueryString?: string; + settingsUpdateError?: AxiosError; + settingsUpdateStatus?: FetchStatus; } type MappingsProps = MappingsOwnProps; @@ -50,9 +52,12 @@ const baseQuery: Query = { const TagMappings: React.FC = ({ canWrite }) => { const [query, setQuery] = useState({ ...baseQuery }); - const intl = useIntl(); + const { settings, settingsError, settingsStatus, settingsUpdateError, settingsUpdateStatus } = useMapToProps({ + query, + }); - const { settings, settingsError, settingsStatus } = useMapToProps({ query }); + const dispatch: ThunkDispatch = useDispatch(); + const intl = useIntl(); const getMappings = () => { if (settings) { @@ -63,7 +68,7 @@ const TagMappings: React.FC = ({ canWrite }) => { const getPagination = (isDisabled = false, isBottom = false) => { const count = settings?.meta ? settings.meta.count : 0; - const limit = settings?.meta ? settings.meta.limit : baseQuery.limit; + const limit = settings?.meta?.limit ? settings.meta.limit : baseQuery.limit; const offset = settings?.meta ? settings.meta.offset : baseQuery.offset; const page = Math.trunc(offset / limit + 1); @@ -83,7 +88,7 @@ const TagMappings: React.FC = ({ canWrite }) => { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; @@ -93,8 +98,10 @@ const TagMappings: React.FC = ({ canWrite }) => { handleOnSort(sortType, isSortAscending)} settings={settings} /> @@ -112,13 +119,23 @@ const TagMappings: React.FC = ({ canWrite }) => { itemsTotal={itemsTotal} onFilterAdded={filter => handleOnFilterAdded(filter)} onFilterRemoved={filter => handleOnFilterRemoved(filter)} - onWizardClose={handleOnWizardClose} + onWizardClose={refresh} pagination={getPagination(isDisabled)} query={query} /> ); }; + const handleOnDeleteChild = (item: SettingsData) => { + if (settingsUpdateStatus !== FetchStatus.inProgress) { + dispatch( + settingsActions.updateSettings(SettingsType.tagsMappingsChildRemove, { + ids: [item.uuid], + }) + ); + } + }; + const handleOnFilterAdded = filter => { const newQuery = queryUtils.handleOnFilterAdded(query, filter); setQuery(newQuery); @@ -144,17 +161,23 @@ const TagMappings: React.FC = ({ canWrite }) => { setQuery(newQuery); }; - const handleOnWizardClose = () => { + const refresh = () => { setQuery({ ...query }); // Force refresh }; + useEffect(() => { + if (settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { + refresh(); + } + }, [settingsUpdateError, settingsUpdateStatus]); + if (settingsError) { return ; } const mappings = getMappings(); const isDisabled = mappings.length === 0; - const hasMappings = mappings.length > 0 && !Object.keys(query.filter_by).length; // no filter applied + const hasMappings = mappings.length > 0 || Object.keys(query.filter_by || {}).length > 0; // filter may be applied return ( <> @@ -178,7 +201,7 @@ const TagMappings: React.FC = ({ canWrite }) => { ) : (
- +
)} @@ -206,6 +229,13 @@ const useMapToProps = ({ query }: MappingsMapProps): MappingsStateProps => { settingsSelectors.selectSettingsError(state, SettingsType.tagsMappings, settingsQueryString) ); + const settingsUpdateStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildRemove) + ); + const settingsUpdateError = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateError(state, SettingsType.tagsMappingsChildRemove) + ); + useEffect(() => { if (!settingsError && settingsStatus !== FetchStatus.inProgress) { dispatch(settingsActions.fetchSettings(SettingsType.tagsMappings, settingsQueryString)); @@ -217,6 +247,8 @@ const useMapToProps = ({ query }: MappingsMapProps): MappingsStateProps => { settingsError, settingsStatus, settingsQueryString, + settingsUpdateError, + settingsUpdateStatus, }; }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx index f66ac36f1..ec5cfd288 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx @@ -1,20 +1,26 @@ import 'routes/components/dataTable/dataTable.scss'; -import { Label } from '@patternfly/react-core'; +import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; +import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; import type { Settings } from 'api/settings'; +import type { SettingsData } from 'api/settings'; import messages from 'locales/messages'; import React from 'react'; import type { WrappedComponentProps } from 'react-intl'; import { injectIntl } from 'react-intl'; -import { DataTable } from 'routes/components/dataTable'; +import { ExpandableTable } from 'routes/components/dataTable'; import type { RouterComponentProps } from 'utils/router'; import { withRouter } from 'utils/router'; +import { styles } from './tagMappings.styles'; + interface TagMappingsTableOwnProps extends RouterComponentProps, WrappedComponentProps { canWrite?: boolean; filterBy?: any; isAllSelected?: boolean; + isDisabled?: boolean; isLoading?: boolean; + onDeleteChild(item: SettingsData); onSort(value: string, isSortAscending: boolean); orderBy?: any; settings: Settings; @@ -58,13 +64,11 @@ class TagMappingsTableBase extends React.Component { + const parent = item.parent; rows.push({ cells: [ + {}, // Empty cell for expand toggle { - value: item.key ? item.key : '', + value: parent.key ? parent.key : '', }, { - value: item.enabled ? ( - - ) : ( - - ), + value: intl.formatMessage(messages.sourceTypes, { value: parent?.source_type?.toLowerCase() }), }, { - value: intl.formatMessage(messages.sourceTypes, { value: item?.source_type?.toLowerCase() }), + value: 'Test...', }, ], - item, + children: parent.children.map(child => { + return { + cells: [ + {}, // Empty cell for expand toggle + { + value: child.key ? child.key : '', + style: styles.expandableRowContent, + }, + { + value: intl.formatMessage(messages.sourceTypes, { value: child?.source_type?.toLowerCase() }), + style: styles.expandableRowContent, + }, + { + value: this.getChildActions(child), + }, + ], + item: child, + }; + }), + item: parent, }); }); @@ -107,14 +131,42 @@ class TagMappingsTableBase extends React.Component { + const { canWrite, intl, isDisabled, onDeleteChild } = this.props; + + const getTooltip = children => { + if (!canWrite) { + const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); + return {children}; + } + return children; + }; + + return getTooltip( + + ); + }; + public render() { const { filterBy, isLoading, onSort, orderBy } = this.props; const { columns, rows } = this.state; + const isAllExpanded = filterBy ? Object.keys(filterBy).find(key => key === 'child') : false; + return ( - = ({ isDisabled, onClose, }: TagMappingsWizardProps) => { - const [isOpen, setIsOpen] = useState(false); - const [isFinished, setIsFinished] = useState(false); const [childTags, setChildTags] = useState([]); + const [isFinished, setIsFinished] = useState(false); + const [isOpen, setIsOpen] = useState(false); const [parentTags, setParentTags] = useState([]); const dispatch: ThunkDispatch = useDispatch(); const intl = useIntl(); - const { settingsError, settingsStatus } = useMapToProps(); + const { settingsUpdateError, settingsUpdateStatus } = useMapToProps(); const getActions = () => { const getTooltip = children => { @@ -57,7 +57,7 @@ const TagMappingsWizard: React.FC = ({ }; return getTooltip( - ); @@ -128,8 +128,8 @@ const TagMappingsWizard: React.FC = ({
@@ -146,15 +146,15 @@ const TagMappingsWizard: React.FC = ({ }; const handleOnClose = () => { + handleOnReset(); setIsOpen(false); - setIsFinished(false); if (onClose) { onClose(); } }; const handleOnCreateTagMapping = () => { - if (settingsStatus !== FetchStatus.inProgress) { + if (settingsUpdateStatus !== FetchStatus.inProgress) { dispatch( settingsActions.updateSettings(SettingsType.tagsMappingsChildAdd, { parent: parentTags.length ? parentTags[0].uuid : undefined, @@ -165,7 +165,10 @@ const TagMappingsWizard: React.FC = ({ }; const handleOnReset = () => { + setChildTags([]); + setParentTags([]); setIsFinished(false); + dispatch(settingsActions.resetSettingsState()); }; const handleOnSelectChild = (items: SettingsData[], isSelected: boolean = false) => { @@ -197,10 +200,10 @@ const TagMappingsWizard: React.FC = ({ }; useEffect(() => { - if (settingsStatus === FetchStatus.complete && !settingsError) { + if (settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { setIsFinished(true); } - }, [settingsError, settingsStatus]); + }, [settingsUpdateError, settingsUpdateStatus]); return ( <> @@ -211,16 +214,16 @@ const TagMappingsWizard: React.FC = ({ }; const useMapToProps = (): TagMappingsWizardStateProps => { - const settingsStatus = useSelector((state: RootState) => + const settingsUpdateStatus = useSelector((state: RootState) => settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildAdd) ); - const settingsError = useSelector((state: RootState) => + const settingsUpdateError = useSelector((state: RootState) => settingsSelectors.selectSettingsUpdateError(state, SettingsType.tagsMappingsChildAdd) ); return { - settingsError, - settingsStatus, + settingsUpdateError, + settingsUpdateStatus, }; }; diff --git a/src/store/settings/settingsActions.ts b/src/store/settings/settingsActions.ts index caf9f5a09..7a62f7112 100644 --- a/src/store/settings/settingsActions.ts +++ b/src/store/settings/settingsActions.ts @@ -28,6 +28,8 @@ export const updateSettingsSuccess = createAction('settings/update/success')< >(); export const updateSettingsFailure = createAction('settings/update/failure')(); +export const resetSettingsState = createAction('settings/reset_state')(); + export function fetchSettings(settingsType: SettingsType, settingsQueryString: string): ThunkAction { return (dispatch, getState) => { const state = getState(); diff --git a/src/store/settings/settingsReducer.ts b/src/store/settings/settingsReducer.ts index 9adfef48f..161c86b7a 100644 --- a/src/store/settings/settingsReducer.ts +++ b/src/store/settings/settingsReducer.ts @@ -5,7 +5,12 @@ import { resetState } from 'store/ui/uiActions'; import type { ActionType } from 'typesafe-actions'; import { getType } from 'typesafe-actions'; -import { updateSettingsFailure, updateSettingsRequest, updateSettingsSuccess } from './settingsActions'; +import { + resetSettingsState, + updateSettingsFailure, + updateSettingsRequest, + updateSettingsSuccess, +} from './settingsActions'; import { fetchSettingsFailure, fetchSettingsRequest, fetchSettingsSuccess } from './settingsActions'; export type SettingsState = Readonly<{ @@ -27,12 +32,14 @@ export type SettingsAction = ActionType< | typeof updateSettingsFailure | typeof updateSettingsRequest | typeof updateSettingsSuccess + | typeof resetSettingsState | typeof resetState >; export function settingsReducer(state = defaultState, action: SettingsAction): SettingsState { switch (action.type) { case getType(resetState): + case getType(resetSettingsState): state = defaultState; return state; From d888681422dc376bb914525c14398fee9eff856a Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Sat, 9 Mar 2024 22:44:28 -0500 Subject: [PATCH 29/61] Renamed pagination widgetId --- src/routes/details/awsDetails/awsDetails.tsx | 2 +- src/routes/details/azureDetails/azureDetails.tsx | 2 +- src/routes/details/components/pvcChart/modal/pvcContent.tsx | 2 +- src/routes/details/gcpDetails/gcpDetails.tsx | 2 +- src/routes/details/ibmDetails/ibmDetails.tsx | 2 +- src/routes/details/ociDetails/ociDetails.tsx | 2 +- src/routes/details/ocpDetails/ocpDetails.tsx | 2 +- src/routes/details/rhelDetails/rhelDetails.tsx | 2 +- src/routes/explorer/explorer.tsx | 2 +- src/routes/settings/costCategory/costCategory.tsx | 2 +- src/routes/settings/platformProjects/platformProjects.tsx | 2 +- .../tagLabels/tagMappings/components/childTags/childTags.tsx | 2 +- .../tagLabels/tagMappings/components/parentTags/parentTags.tsx | 2 +- src/routes/settings/tagLabels/tags/tags.tsx | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/routes/details/awsDetails/awsDetails.tsx b/src/routes/details/awsDetails/awsDetails.tsx index 2b9f40a7c..cf7669bd3 100644 --- a/src/routes/details/awsDetails/awsDetails.tsx +++ b/src/routes/details/awsDetails/awsDetails.tsx @@ -206,7 +206,7 @@ class AwsDetails extends React.Component { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/details/azureDetails/azureDetails.tsx b/src/routes/details/azureDetails/azureDetails.tsx index e37b687e0..63baa3c72 100644 --- a/src/routes/details/azureDetails/azureDetails.tsx +++ b/src/routes/details/azureDetails/azureDetails.tsx @@ -193,7 +193,7 @@ class AzureDetails extends React.Component }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/details/components/pvcChart/modal/pvcContent.tsx b/src/routes/details/components/pvcChart/modal/pvcContent.tsx index fb6848a54..993897226 100644 --- a/src/routes/details/components/pvcChart/modal/pvcContent.tsx +++ b/src/routes/details/components/pvcChart/modal/pvcContent.tsx @@ -91,7 +91,7 @@ const PvcContent: React.FC = () => { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/details/gcpDetails/gcpDetails.tsx b/src/routes/details/gcpDetails/gcpDetails.tsx index 96fdc4920..7d8ccca04 100644 --- a/src/routes/details/gcpDetails/gcpDetails.tsx +++ b/src/routes/details/gcpDetails/gcpDetails.tsx @@ -193,7 +193,7 @@ class GcpDetails extends React.Component { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/details/ibmDetails/ibmDetails.tsx b/src/routes/details/ibmDetails/ibmDetails.tsx index ec5401523..e37f7cf5d 100644 --- a/src/routes/details/ibmDetails/ibmDetails.tsx +++ b/src/routes/details/ibmDetails/ibmDetails.tsx @@ -194,7 +194,7 @@ class IbmDetails extends React.Component { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/details/ociDetails/ociDetails.tsx b/src/routes/details/ociDetails/ociDetails.tsx index f67e5d649..e71481ee1 100644 --- a/src/routes/details/ociDetails/ociDetails.tsx +++ b/src/routes/details/ociDetails/ociDetails.tsx @@ -193,7 +193,7 @@ class OciDetails extends React.Component { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/details/ocpDetails/ocpDetails.tsx b/src/routes/details/ocpDetails/ocpDetails.tsx index 2e89bc066..47b496c2d 100644 --- a/src/routes/details/ocpDetails/ocpDetails.tsx +++ b/src/routes/details/ocpDetails/ocpDetails.tsx @@ -242,7 +242,7 @@ class OcpDetails extends React.Component { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/details/rhelDetails/rhelDetails.tsx b/src/routes/details/rhelDetails/rhelDetails.tsx index ef2bb47f2..76a42e0a5 100644 --- a/src/routes/details/rhelDetails/rhelDetails.tsx +++ b/src/routes/details/rhelDetails/rhelDetails.tsx @@ -236,7 +236,7 @@ class RhelDetails extends React.Component { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/explorer/explorer.tsx b/src/routes/explorer/explorer.tsx index 841d254f5..587c90607 100644 --- a/src/routes/explorer/explorer.tsx +++ b/src/routes/explorer/explorer.tsx @@ -243,7 +243,7 @@ class Explorer extends React.Component { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/settings/costCategory/costCategory.tsx b/src/routes/settings/costCategory/costCategory.tsx index f30de1546..c90466927 100644 --- a/src/routes/settings/costCategory/costCategory.tsx +++ b/src/routes/settings/costCategory/costCategory.tsx @@ -86,7 +86,7 @@ const CostCategory: React.FC = ({ canWrite }) => { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/settings/platformProjects/platformProjects.tsx b/src/routes/settings/platformProjects/platformProjects.tsx index cb785b559..1222683d6 100644 --- a/src/routes/settings/platformProjects/platformProjects.tsx +++ b/src/routes/settings/platformProjects/platformProjects.tsx @@ -86,7 +86,7 @@ const PlatformProjects: React.FC = ({ canWrite }) => { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx index b594ad2a9..e26089872 100644 --- a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx +++ b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx @@ -83,7 +83,7 @@ const ChildTags: React.FC = ({ onBulkSelect, onSelect, selectedI }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTags.tsx b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTags.tsx index bbf24e0fb..8424b972f 100644 --- a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTags.tsx +++ b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTags.tsx @@ -82,7 +82,7 @@ const ParentTags: React.FC = ({ onSelect, selectedItems }: Pare }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; diff --git a/src/routes/settings/tagLabels/tags/tags.tsx b/src/routes/settings/tagLabels/tags/tags.tsx index 3af2d0826..6193555c0 100644 --- a/src/routes/settings/tagLabels/tags/tags.tsx +++ b/src/routes/settings/tagLabels/tags/tags.tsx @@ -85,7 +85,7 @@ const Tags: React.FC = ({ canWrite }) => { }), }} variant={isBottom ? PaginationVariant.bottom : PaginationVariant.top} - widgetId={`exports-pagination${isBottom ? '-bottom' : ''}`} + widgetId={`pagination${isBottom ? '-bottom' : ''}`} /> ); }; From 28388047597a07425057da6daf2a967f1f9891c3 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Sun, 10 Mar 2024 13:49:46 -0400 Subject: [PATCH 30/61] Refactored to use functional component --- .../components/childTags/childTagsTable.tsx | 103 ++++----- .../components/parentTags/parentTagsTable.tsx | 106 ++++----- .../tagLabels/tagMappings/tagMappings.tsx | 43 ++-- .../tagMappings/tagMappingsTable.tsx | 206 ++++++++++-------- 4 files changed, 215 insertions(+), 243 deletions(-) diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx index 07a129d70..253423566 100644 --- a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx +++ b/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx @@ -3,14 +3,11 @@ import 'routes/components/dataTable/dataTable.scss'; import type { Settings } from 'api/settings'; import type { SettingsData } from 'api/settings'; import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; import { DataTable } from 'routes/components/dataTable'; -import type { RouterComponentProps } from 'utils/router'; -import { withRouter } from 'utils/router'; -interface ChildTagsTableOwnProps extends RouterComponentProps, WrappedComponentProps { +interface ChildTagsTableOwnProps { filterBy?: any; isAllSelected?: boolean; isLoading?: boolean; @@ -21,43 +18,30 @@ interface ChildTagsTableOwnProps extends RouterComponentProps, WrappedComponentP settings: Settings; } -interface ChildTagsTableState { - columns?: any[]; - rows?: any[]; -} - type ChildTagsTableProps = ChildTagsTableOwnProps; -class ChildTagsTableBase extends React.Component { - public state: ChildTagsTableState = { - columns: [], - rows: [], - }; - - public componentDidMount() { - this.initDatum(); - } - - public componentDidUpdate(prevProps: ChildTagsTableProps) { - const { selectedItems, settings } = this.props; - const currentReport = settings?.data ? JSON.stringify(settings.data) : ''; - const previousReport = prevProps?.settings.data ? JSON.stringify(prevProps.settings.data) : ''; - - if (previousReport !== currentReport || prevProps.selectedItems !== selectedItems) { - this.initDatum(); - } - } - - private initDatum = () => { - const { intl, selectedItems, settings } = this.props; +const ChildTagsTable: React.FC = ({ + filterBy, + isLoading, + onSelect, + onSort, + orderBy, + selectedItems, + settings, +}) => { + const [columns, setColumns] = useState([]); + const [rows, setRows] = useState([]); + const intl = useIntl(); + + const initDatum = () => { if (!settings) { return; } - const rows = []; + const newRows = []; const tags = settings?.data ? (settings.data as any) : []; - const columns = [ + const newColumns = [ // Sorting with tag keys is not supported { name: '', @@ -75,7 +59,7 @@ class ChildTagsTableBase extends React.Component { - rows.push({ + newRows.push({ cells: [ { name: '', @@ -92,37 +76,32 @@ class ChildTagsTableBase extends React.Component !column.hidden); - const filteredRows = rows.map(({ ...row }) => { + const filteredColumns = (newColumns as any[]).filter(column => !column.hidden); + const filteredRows = newRows.map(({ ...row }) => { row.cells = row.cells.filter(cell => !cell.hidden); return row; }); - this.setState({ - columns: filteredColumns, - rows: filteredRows, - }); + setColumns(filteredColumns); + setRows(filteredRows); }; - public render() { - const { filterBy, isLoading, onSelect, onSort, orderBy } = this.props; - const { columns, rows } = this.state; - - return ( - - ); - } -} - -const ChildTagsTable = injectIntl(withRouter(ChildTagsTableBase)); + useEffect(() => { + initDatum(); + }, [selectedItems, settings]); + + return ( + + ); +}; export { ChildTagsTable }; diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx index f428bcfe2..3ea2cb457 100644 --- a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx +++ b/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx @@ -3,16 +3,12 @@ import 'routes/components/dataTable/dataTable.scss'; import type { Settings } from 'api/settings'; import type { SettingsData } from 'api/settings'; import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; import { DataTable } from 'routes/components/dataTable'; -import type { RouterComponentProps } from 'utils/router'; -import { withRouter } from 'utils/router'; -interface ParentTagsTableOwnProps extends RouterComponentProps, WrappedComponentProps { +interface ParentTagsTableOwnProps { filterBy?: any; - isAllSelected?: boolean; isLoading?: boolean; onSelect(items: SettingsData[], isSelected: boolean); onSort(value: string, isSortAscending: boolean); @@ -21,43 +17,30 @@ interface ParentTagsTableOwnProps extends RouterComponentProps, WrappedComponent settings: Settings; } -interface ParentTagsTableState { - columns?: any[]; - rows?: any[]; -} - type ParentTagsTableProps = ParentTagsTableOwnProps; -class ParentTagsTableBase extends React.Component { - public state: ParentTagsTableState = { - columns: [], - rows: [], - }; - - public componentDidMount() { - this.initDatum(); - } - - public componentDidUpdate(prevProps: ParentTagsTableProps) { - const { selectedItems, settings } = this.props; - const currentReport = settings?.data ? JSON.stringify(settings.data) : ''; - const previousReport = prevProps?.settings.data ? JSON.stringify(prevProps.settings.data) : ''; - - if (previousReport !== currentReport || prevProps.selectedItems !== selectedItems) { - this.initDatum(); - } - } - - private initDatum = () => { - const { intl, selectedItems, settings } = this.props; +const ParentTagsTable: React.FC = ({ + filterBy, + isLoading, + onSelect, + onSort, + orderBy, + selectedItems, + settings, +}) => { + const [columns, setColumns] = useState([]); + const [rows, setRows] = useState([]); + const intl = useIntl(); + + const initDatum = () => { if (!settings) { return; } - const rows = []; + const newRows = []; const tags = settings?.data ? (settings.data as any) : []; - const columns = [ + const newColumns = [ // Sorting with tag keys is not supported { name: '', @@ -75,7 +58,7 @@ class ParentTagsTableBase extends React.Component { - rows.push({ + newRows.push({ cells: [ { name: '', @@ -92,38 +75,33 @@ class ParentTagsTableBase extends React.Component !column.hidden); - const filteredRows = rows.map(({ ...row }) => { + const filteredColumns = (newColumns as any[]).filter(column => !column.hidden); + const filteredRows = newRows.map(({ ...row }) => { row.cells = row.cells.filter(cell => !cell.hidden); return row; }); - this.setState({ - columns: filteredColumns, - rows: filteredRows, - }); + setColumns(filteredColumns); + setRows(filteredRows); }; - public render() { - const { filterBy, isLoading, onSelect, onSort, orderBy } = this.props; - const { columns, rows } = this.state; - - return ( - - ); - } -} - -const ParentTagsTable = injectIntl(withRouter(ParentTagsTableBase)); + useEffect(() => { + initDatum(); + }, [selectedItems, settings]); + + return ( + + ); +}; export { ParentTagsTable }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx index 24d295453..249a27ca4 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx @@ -35,8 +35,6 @@ export interface MappingsStateProps { settingsError?: AxiosError; settingsStatus?: FetchStatus; settingsQueryString?: string; - settingsUpdateError?: AxiosError; - settingsUpdateStatus?: FetchStatus; } type MappingsProps = MappingsOwnProps; @@ -52,7 +50,7 @@ const baseQuery: Query = { const TagMappings: React.FC = ({ canWrite }) => { const [query, setQuery] = useState({ ...baseQuery }); - const { settings, settingsError, settingsStatus, settingsUpdateError, settingsUpdateStatus } = useMapToProps({ + const { settings, settingsError, settingsStatus } = useMapToProps({ query, }); @@ -100,9 +98,9 @@ const TagMappings: React.FC = ({ canWrite }) => { filterBy={query.filter_by} isDisabled={isDisabled} isLoading={settingsStatus === FetchStatus.inProgress} - orderBy={query.order_by} - onDeleteChild={handleOnDeleteChild} + onDelete={handleOnDelete} onSort={(sortType, isSortAscending) => handleOnSort(sortType, isSortAscending)} + orderBy={query.order_by} settings={settings} /> ); @@ -119,21 +117,20 @@ const TagMappings: React.FC = ({ canWrite }) => { itemsTotal={itemsTotal} onFilterAdded={filter => handleOnFilterAdded(filter)} onFilterRemoved={filter => handleOnFilterRemoved(filter)} - onWizardClose={refresh} + onWizardClose={handleOnClose} pagination={getPagination(isDisabled)} query={query} /> ); }; - const handleOnDeleteChild = (item: SettingsData) => { - if (settingsUpdateStatus !== FetchStatus.inProgress) { - dispatch( - settingsActions.updateSettings(SettingsType.tagsMappingsChildRemove, { - ids: [item.uuid], - }) - ); - } + const handleOnClose = () => { + refresh(); + }; + + const handleOnDelete = () => { + dispatch(settingsActions.resetSettingsState()); + refresh(); }; const handleOnFilterAdded = filter => { @@ -161,16 +158,11 @@ const TagMappings: React.FC = ({ canWrite }) => { setQuery(newQuery); }; + // Force refresh const refresh = () => { - setQuery({ ...query }); // Force refresh + setQuery({ ...query }); }; - useEffect(() => { - if (settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { - refresh(); - } - }, [settingsUpdateError, settingsUpdateStatus]); - if (settingsError) { return ; } @@ -229,13 +221,6 @@ const useMapToProps = ({ query }: MappingsMapProps): MappingsStateProps => { settingsSelectors.selectSettingsError(state, SettingsType.tagsMappings, settingsQueryString) ); - const settingsUpdateStatus = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildRemove) - ); - const settingsUpdateError = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateError(state, SettingsType.tagsMappingsChildRemove) - ); - useEffect(() => { if (!settingsError && settingsStatus !== FetchStatus.inProgress) { dispatch(settingsActions.fetchSettings(SettingsType.tagsMappings, settingsQueryString)); @@ -247,8 +232,6 @@ const useMapToProps = ({ query }: MappingsMapProps): MappingsStateProps => { settingsError, settingsStatus, settingsQueryString, - settingsUpdateError, - settingsUpdateStatus, }; }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx index ec5cfd288..0b3497330 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx @@ -4,86 +4,128 @@ import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; import type { Settings } from 'api/settings'; import type { SettingsData } from 'api/settings'; +import { SettingsType } from 'api/settings'; +import type { AxiosError } from 'axios'; import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; import { ExpandableTable } from 'routes/components/dataTable'; -import type { RouterComponentProps } from 'utils/router'; -import { withRouter } from 'utils/router'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { settingsActions, settingsSelectors } from 'store/settings'; import { styles } from './tagMappings.styles'; -interface TagMappingsTableOwnProps extends RouterComponentProps, WrappedComponentProps { +interface TagMappingsTableOwnProps { canWrite?: boolean; filterBy?: any; - isAllSelected?: boolean; isDisabled?: boolean; isLoading?: boolean; - onDeleteChild(item: SettingsData); + onDelete(); onSort(value: string, isSortAscending: boolean); orderBy?: any; settings: Settings; } -interface TagMappingsTableState { - columns?: any[]; - rows?: any[]; +export interface TagMappingsTableStateProps { + settingsUpdateError?: AxiosError; + settingsUpdateStatus?: FetchStatus; } type TagMappingsTableProps = TagMappingsTableOwnProps; -class TagMappingsTableBase extends React.Component { - public state: TagMappingsTableState = { - columns: [], - rows: [], - }; - - public componentDidMount() { - this.initDatum(); - } +const TagMappingsTable: React.FC = ({ + canWrite, + filterBy, + isDisabled, + isLoading, + onDelete, + onSort, + orderBy, + settings, +}) => { + const [columns, setColumns] = useState([]); + const [rows, setRows] = useState([]); + const { settingsUpdateError, settingsUpdateStatus } = useMapToProps(); + + const dispatch: ThunkDispatch = useDispatch(); + const intl = useIntl(); + + const getChildActions = (item: SettingsData) => { + const getTooltip = children => { + if (!canWrite) { + const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); + return {children}; + } + return children; + }; - public componentDidUpdate(prevProps: TagMappingsTableProps) { - const { settings } = this.props; - const currentReport = settings?.data ? JSON.stringify(settings.data) : ''; - const previousReport = prevProps?.settings.data ? JSON.stringify(prevProps.settings.data) : ''; + return getTooltip( + + ); + }; - if (previousReport !== currentReport) { - this.initDatum(); + const handleOnDeleteChild = (item: SettingsData) => { + if (settingsUpdateStatus !== FetchStatus.inProgress) { + dispatch( + settingsActions.updateSettings(SettingsType.tagsMappingsChildRemove, { + ids: [item.uuid], + }) + ); } - } + }; - private initDatum = () => { - const { intl, settings } = this.props; + // const handleOnDeleteParent = (item: SettingsData) => { + // if (settingsUpdateStatus !== FetchStatus.inProgress) { + // dispatch( + // settingsActions.updateSettings(SettingsType.tagsMappingsParentRemove, { + // ids: [item.uuid], + // }) + // ); + // } + // }; + + const initDatum = () => { if (!settings) { return; } - const rows = []; - const tags = settings?.data ? (settings.data as any) : []; + const newRows = []; + const tagMappings = settings?.data ? (settings.data as any) : []; - const columns = [ + const newColumns = [ { name: '', }, { orderBy: 'parent', name: intl.formatMessage(messages.detailsResourceNames, { value: 'tag_key' }), - ...(tags.length && { isSortable: true }), + ...(tagMappings.length && { isSortable: true }), }, { orderBy: 'source_type', name: intl.formatMessage(messages.sourceType), - ...(tags.length && { isSortable: true }), + ...(tagMappings.length && { isSortable: true }), }, { name: '', }, ]; - tags.map(item => { + tagMappings.map(item => { const parent = item.parent; - rows.push({ + newRows.push({ cells: [ {}, // Empty cell for expand toggle { @@ -109,7 +151,7 @@ class TagMappingsTableBase extends React.Component !column.hidden); - const filteredRows = rows.map(({ ...row }) => { + const filteredColumns = (newColumns as any[]).filter(column => !column.hidden); + const filteredRows = newRows.map(({ ...row }) => { row.cells = row.cells.filter(cell => !cell.hidden); return row; }); - this.setState({ - columns: filteredColumns, - rows: filteredRows, - }); + setColumns(filteredColumns); + setRows(filteredRows); }; - private getChildActions = (item: SettingsData) => { - const { canWrite, intl, isDisabled, onDeleteChild } = this.props; + useEffect(() => { + initDatum(); + }, [settings]); - const getTooltip = children => { - if (!canWrite) { - const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); - return {children}; - } - return children; - }; - - return getTooltip( - - ); + useEffect(() => { + if (settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { + onDelete(); + } + }, [settingsUpdateError, settingsUpdateStatus]); + + return ( + key === 'child') : false} + isLoading={isLoading} + onSort={onSort} + orderBy={orderBy} + rows={rows} + /> + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = (): TagMappingsTableStateProps => { + const settingsUpdateStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildRemove) + ); + const settingsUpdateError = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateError(state, SettingsType.tagsMappingsChildRemove) + ); + + return { + settingsUpdateError, + settingsUpdateStatus, }; - - public render() { - const { filterBy, isLoading, onSort, orderBy } = this.props; - const { columns, rows } = this.state; - - const isAllExpanded = filterBy ? Object.keys(filterBy).find(key => key === 'child') : false; - - return ( - - ); - } -} - -const TagMappingsTable = injectIntl(withRouter(TagMappingsTableBase)); +}; export { TagMappingsTable }; From 96299f484a2e3d59346a81c6c13206f7a663947f Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Sun, 10 Mar 2024 15:20:05 -0400 Subject: [PATCH 31/61] More refactoring to use functional components --- .../settings/calculations/calculations.tsx | 143 +++++++---------- .../costCategory/costCategoryTable.tsx | 110 ++++++------- .../costCategory/costCategoryToolbar.tsx | 147 ++++++----------- .../platformProjectsTable.tsx | 111 ++++++------- .../platformProjectsToolbar.tsx | 142 ++++++----------- .../components/childTags/childTagsToolbar.tsx | 117 ++++---------- .../parentTags/parentTagsToolbar.tsx | 109 ++++--------- .../tagMappings/tagMappingsToolbar.tsx | 115 ++++---------- .../settings/tagLabels/tags/tagsTable.tsx | 107 +++++-------- .../settings/tagLabels/tags/tagsToolbar.tsx | 150 ++++++------------ 10 files changed, 426 insertions(+), 825 deletions(-) diff --git a/src/routes/settings/calculations/calculations.tsx b/src/routes/settings/calculations/calculations.tsx index 15bd4a982..34053b4ab 100644 --- a/src/routes/settings/calculations/calculations.tsx +++ b/src/routes/settings/calculations/calculations.tsx @@ -1,57 +1,41 @@ import { PageSection, Title, TitleSizes, Tooltip } from '@patternfly/react-core'; import { AccountSettingsType } from 'api/accountSettings'; import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; import { CostType } from 'routes/components/costType'; import { Currency } from 'routes/components/currency'; +import type { RootState } from 'store'; import { accountSettingsActions, accountSettingsSelectors } from 'store/accountSettings'; import type { FetchStatus } from 'store/common'; -import { createMapStateToProps } from 'store/common'; -import type { RouterComponentProps } from 'utils/router'; -import { withRouter } from 'utils/router'; import { getAccountCostType, getAccountCurrency } from 'utils/sessionStorage'; import { styles } from './calculations.styles'; -interface SettingsOwnProps { +interface CalculationsPropsOwnProps { canWrite?: boolean; } -interface SettingsDispatchProps { - updateCostType: typeof accountSettingsActions.updateAccountSettings; - updateCurrency: typeof accountSettingsActions.updateAccountSettings; +export interface CalculationsStateProps { + costTypeAccountSettingsUpdateStatus: FetchStatus; + currencyAccountSettingsUpdateStatus: FetchStatus; } -interface SettingsStateProps { - updateCostTypeStatus: FetchStatus; - updateCurrencyStatus: FetchStatus; -} +type CalculationsProps = CalculationsPropsOwnProps; -interface SettingsState { - currentCostType?: string; - currentCurrency?: string; -} +const Calculations: React.FC = ({ canWrite }) => { + const [costType, setCostType] = useState(getAccountCostType()); + const [currency, setCurrency] = useState(getAccountCostType()); -type SettingsProps = SettingsOwnProps & - SettingsStateProps & - SettingsDispatchProps & - RouterComponentProps & - WrappedComponentProps; + const dispatch: ThunkDispatch = useDispatch(); + const intl = useIntl(); -class SettingsBase extends React.Component { - protected defaultState: SettingsState = { - currentCostType: getAccountCostType(), - currentCurrency: getAccountCurrency(), - }; - public state: SettingsState = { ...this.defaultState }; - - private getCostType = () => { - const { canWrite, intl } = this.props; - const { currentCostType } = this.state; + const { costTypeAccountSettingsUpdateStatus, currencyAccountSettingsUpdateStatus } = useMapToProps(); + const getCostType = () => { return (
@@ -59,12 +43,12 @@ class SettingsBase extends React.Component<SettingsProps, SettingsState> { {intl.formatMessage(messages.costTypeSettingsDesc)}
- {this.getTooltip( + {getTooltip( )} @@ -73,10 +57,7 @@ class SettingsBase extends React.Component { ); }; - private getCurrency = () => { - const { canWrite, intl } = this.props; - const { currentCurrency } = this.state; - + const getCurrency = () => { return (
@@ -84,12 +65,12 @@ class SettingsBase extends React.Component<SettingsProps, SettingsState> { {intl.formatMessage(messages.currencyDesc)}
- {this.getTooltip( + {getTooltip( )} @@ -98,64 +79,52 @@ class SettingsBase extends React.Component { ); }; - private getTooltip = comp => { - const { canWrite, intl } = this.props; - + const getTooltip = comp => { return !canWrite ? {comp} : comp; }; - private handleOnCostTypeSelected = value => { - const { updateCostType } = this.props; - - this.setState({ currentCostType: value }, () => { - updateCostType(AccountSettingsType.costType, { + const handleOnCostTypeSelected = value => { + dispatch( + accountSettingsActions.updateAccountSettings(AccountSettingsType.costType, { cost_type: value, - }); - }); + }) + ); }; - private handleOnCurrencySelected = value => { - const { updateCurrency } = this.props; - - this.setState({ currentCurrency: value }, () => { - updateCurrency(AccountSettingsType.currency, { + const handleOnCurrencySelected = value => { + dispatch( + accountSettingsActions.updateAccountSettings(AccountSettingsType.currency, { currency: value, - }); - }); + }) + ); }; - public render() { - return ( - - {this.getCurrency()} - {this.getCostType()} - - ); - } -} + useEffect(() => { + setCostType(getAccountCostType()); + setCurrency(getAccountCurrency()); + }, [costTypeAccountSettingsUpdateStatus, currencyAccountSettingsUpdateStatus]); -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const mapStateToProps = createMapStateToProps(state => { - const updateCostTypeStatus = accountSettingsSelectors.selectAccountSettingsUpdateStatus( - state, - AccountSettingsType.costType + return ( + + {getCurrency()} + {getCostType()} + ); - const updateCurrencyStatus = accountSettingsSelectors.selectAccountSettingsUpdateStatus( - state, - AccountSettingsType.currency +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = (): CalculationsStateProps => { + const costTypeAccountSettingsUpdateStatus = useSelector((state: RootState) => + accountSettingsSelectors.selectAccountSettingsUpdateStatus(state, AccountSettingsType.costType) + ); + const currencyAccountSettingsUpdateStatus = useSelector((state: RootState) => + accountSettingsSelectors.selectAccountSettingsUpdateStatus(state, AccountSettingsType.currency) ); return { - updateCostTypeStatus, - updateCurrencyStatus, + costTypeAccountSettingsUpdateStatus, + currencyAccountSettingsUpdateStatus, }; -}); - -const mapDispatchToProps: SettingsDispatchProps = { - updateCostType: accountSettingsActions.updateAccountSettings, - updateCurrency: accountSettingsActions.updateAccountSettings, }; -const Calculations = injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(SettingsBase))); - export default Calculations; diff --git a/src/routes/settings/costCategory/costCategoryTable.tsx b/src/routes/settings/costCategory/costCategoryTable.tsx index baa03d337..3eeaa1d81 100644 --- a/src/routes/settings/costCategory/costCategoryTable.tsx +++ b/src/routes/settings/costCategory/costCategoryTable.tsx @@ -3,14 +3,11 @@ import 'routes/components/dataTable/dataTable.scss'; import { Label } from '@patternfly/react-core'; import type { Settings, SettingsData } from 'api/settings'; import messages from 'locales/messages'; -import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; import { DataTable } from 'routes/components/dataTable'; -import type { RouterComponentProps } from 'utils/router'; -import { withRouter } from 'utils/router'; -interface CostCategoryOwnProps extends RouterComponentProps, WrappedComponentProps { +interface CostCategoryTableOwnProps { canWrite?: boolean; filterBy?: any; isLoading?: boolean; @@ -21,43 +18,31 @@ interface CostCategoryOwnProps extends RouterComponentProps, WrappedComponentPro settings: Settings; } -interface CostCategoryState { - columns?: any[]; - rows?: any[]; -} - -type CostCategoryProps = CostCategoryOwnProps; - -class CostCategoryBase extends React.Component { - public state: CostCategoryState = { - columns: [], - rows: [], - }; - - public componentDidMount() { - this.initDatum(); - } - - public componentDidUpdate(prevProps: CostCategoryProps) { - const { selectedItems, settings } = this.props; - const currentReport = settings?.data ? JSON.stringify(settings.data) : ''; - const previousReport = prevProps?.settings.data ? JSON.stringify(prevProps.settings.data) : ''; - - if (previousReport !== currentReport || prevProps.selectedItems !== selectedItems) { - this.initDatum(); - } - } - - private initDatum = () => { - const { canWrite, intl, selectedItems, settings } = this.props; +type CostCategoryTableProps = CostCategoryTableOwnProps; + +const CostCategoryTable: React.FC = ({ + canWrite, + filterBy, + isLoading, + onSelect, + onSort, + orderBy, + selectedItems, + settings, +}) => { + const [columns, setColumns] = useState([]); + const [rows, setRows] = useState([]); + const intl = useIntl(); + + const initDatum = () => { if (!settings) { return; } - const rows = []; + const newRows = []; const categories = settings?.data ? (settings.data as any) : []; - const columns = [ + const newColumns = [ { name: '', // Selection column }, @@ -74,7 +59,7 @@ class CostCategoryBase extends React.Component { - rows.push({ + newRows.push({ cells: [ {}, // Empty cell for row selection { @@ -94,38 +79,33 @@ class CostCategoryBase extends React.Component !column.hidden); - const filteredRows = rows.map(({ ...row }) => { + const filteredColumns = (newColumns as any[]).filter(column => !column.hidden); + const filteredRows = newRows.map(({ ...row }) => { row.cells = row.cells.filter(cell => !cell.hidden); return row; }); - this.setState({ - columns: filteredColumns, - rows: filteredRows, - }); + setColumns(filteredColumns); + setRows(filteredRows); }; - public render() { - const { filterBy, isLoading, onSelect, onSort, orderBy, selectedItems } = this.props; - const { columns, rows } = this.state; - - return ( - - ); - } -} - -const CostCategoryTable = injectIntl(withRouter(CostCategoryBase)); + useEffect(() => { + initDatum(); + }, [selectedItems, settings]); + + return ( + + ); +}; export { CostCategoryTable }; diff --git a/src/routes/settings/costCategory/costCategoryToolbar.tsx b/src/routes/settings/costCategory/costCategoryToolbar.tsx index b77be50d2..44cba283b 100644 --- a/src/routes/settings/costCategory/costCategoryToolbar.tsx +++ b/src/routes/settings/costCategory/costCategoryToolbar.tsx @@ -4,13 +4,10 @@ import { ResourcePathsType } from 'api/resources/resource'; import type { SettingsData } from 'api/settings'; import messages from 'locales/messages'; import React from 'react'; -import type { WrappedComponentProps } from 'react-intl'; -import { injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; +import { useIntl } from 'react-intl'; import { BasicToolbar } from 'routes/components/dataToolbar'; import type { ToolbarChipGroupExt } from 'routes/components/dataToolbar/utils/common'; import type { Filter } from 'routes/utils/filter'; -import { createMapStateToProps } from 'store/common'; import { styles } from './costCategory.styles'; @@ -32,52 +29,36 @@ interface CostCategoryToolbarOwnProps { showBulkSelectAll?: boolean; } -interface CostCategoryToolbarStateProps { - // TBD... -} - -interface CostCategoryToolbarDispatchProps { - // TBD... -} - -interface CostCategoryToolbarState { - categoryOptions?: ToolbarChipGroupExt[]; -} - -type CostCategoryToolbarProps = CostCategoryToolbarOwnProps & - CostCategoryToolbarStateProps & - CostCategoryToolbarDispatchProps & - WrappedComponentProps; - -export class CostCategoryToolbarBase extends React.Component { - protected defaultState: CostCategoryToolbarState = {}; - public state: CostCategoryToolbarState = { ...this.defaultState }; - - public componentDidMount() { - this.setState({ - categoryOptions: this.getCategoryOptions(), - }); - } - - private getActions = () => { - const { - canWrite, - intl, - isPrimaryActionDisabled, - isSecondaryActionDisabled, - onDisableTags, - onEnableTags, - selectedItems, - } = this.props; - - const isDisabled = !canWrite || selectedItems.length === 0; +type CostCategoryToolbarProps = CostCategoryToolbarOwnProps; + +const CostCategoryToolbar: React.FC = ({ + canWrite, + isDisabled, + isPrimaryActionDisabled, + isSecondaryActionDisabled, + itemsPerPage, + itemsTotal, + onBulkSelect, + onDisableTags, + onEnableTags, + onFilterAdded, + onFilterRemoved, + pagination, + query, + selectedItems, + showBulkSelectAll, +}) => { + const intl = useIntl(); + + const getActions = () => { + const isAriaDisabled = !canWrite || isDisabled || selectedItems.length === 0; const tooltip = intl.formatMessage(!canWrite ? messages.readOnlyPermissions : messages.selectCategories); return ( <> + ); + }; + + const handleOnDelete = () => { + if (settingsUpdateStatus !== FetchStatus.inProgress) { + dispatch( + settingsActions.updateSettings(SettingsType.tagsMappingsChildRemove, { + ids: [item.uuid], + }) + ); + } + }; + + const handleOnClose = () => { + setIsOpen(false); + }; + + const handleOnClick = () => { + setIsOpen(!isOpen); + }; + + useEffect(() => { + if (settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { + onDelete(); + } + }, [settingsUpdateError, settingsUpdateStatus]); + + // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. + // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree + + return ( + <> + {getActions()} + + + {intl.formatMessage(messages.tagMappingsDeleteDesc, { value: {item.key} })} + + + + + + + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = (): DeleteModalStateProps => { + const settingsUpdateStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildRemove) + ); + const settingsUpdateError = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateError(state, SettingsType.tagsMappingsChildRemove) + ); + + return { + settingsUpdateError, + settingsUpdateStatus, + }; +}; + +export default DeleteModal; diff --git a/src/routes/settings/tagLabels/tagMappings/components/deleteModal/index.ts b/src/routes/settings/tagLabels/tagMappings/components/deleteModal/index.ts new file mode 100644 index 000000000..3175295fc --- /dev/null +++ b/src/routes/settings/tagLabels/tagMappings/components/deleteModal/index.ts @@ -0,0 +1 @@ +export { default as TagMappingsModal } from './deleteModal'; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx b/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx index 0b3497330..765fad0f7 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx +++ b/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx @@ -1,22 +1,12 @@ import 'routes/components/dataTable/dataTable.scss'; -import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; -import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; import type { Settings } from 'api/settings'; -import type { SettingsData } from 'api/settings'; -import { SettingsType } from 'api/settings'; -import type { AxiosError } from 'axios'; import messages from 'locales/messages'; import React, { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; -import { useDispatch, useSelector } from 'react-redux'; -import type { AnyAction } from 'redux'; -import type { ThunkDispatch } from 'redux-thunk'; import { ExpandableTable } from 'routes/components/dataTable'; -import type { RootState } from 'store'; -import { FetchStatus } from 'store/common'; -import { settingsActions, settingsSelectors } from 'store/settings'; +import DeleteModal from './components/deleteModal/deleteModal'; import { styles } from './tagMappings.styles'; interface TagMappingsTableOwnProps { @@ -30,11 +20,6 @@ interface TagMappingsTableOwnProps { settings: Settings; } -export interface TagMappingsTableStateProps { - settingsUpdateError?: AxiosError; - settingsUpdateStatus?: FetchStatus; -} - type TagMappingsTableProps = TagMappingsTableOwnProps; const TagMappingsTable: React.FC = ({ @@ -49,53 +34,9 @@ const TagMappingsTable: React.FC = ({ }) => { const [columns, setColumns] = useState([]); const [rows, setRows] = useState([]); - const { settingsUpdateError, settingsUpdateStatus } = useMapToProps(); - const dispatch: ThunkDispatch = useDispatch(); const intl = useIntl(); - const getChildActions = (item: SettingsData) => { - const getTooltip = children => { - if (!canWrite) { - const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); - return {children}; - } - return children; - }; - - return getTooltip( - - ); - }; - - const handleOnDeleteChild = (item: SettingsData) => { - if (settingsUpdateStatus !== FetchStatus.inProgress) { - dispatch( - settingsActions.updateSettings(SettingsType.tagsMappingsChildRemove, { - ids: [item.uuid], - }) - ); - } - }; - - // const handleOnDeleteParent = (item: SettingsData) => { - // if (settingsUpdateStatus !== FetchStatus.inProgress) { - // dispatch( - // settingsActions.updateSettings(SettingsType.tagsMappingsParentRemove, { - // ids: [item.uuid], - // }) - // ); - // } - // }; - const initDatum = () => { if (!settings) { return; @@ -151,7 +92,7 @@ const TagMappingsTable: React.FC = ({ style: styles.expandableRowContent, }, { - value: getChildActions(child), + value: , }, ], item: child, @@ -175,12 +116,6 @@ const TagMappingsTable: React.FC = ({ initDatum(); }, [settings]); - useEffect(() => { - if (settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { - onDelete(); - } - }, [settingsUpdateError, settingsUpdateStatus]); - return ( = ({ ); }; -// eslint-disable-next-line no-empty-pattern -const useMapToProps = (): TagMappingsTableStateProps => { - const settingsUpdateStatus = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildRemove) - ); - const settingsUpdateError = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateError(state, SettingsType.tagsMappingsChildRemove) - ); - - return { - settingsUpdateError, - settingsUpdateStatus, - }; -}; - export { TagMappingsTable }; From 9e0b63ed29b3d94bb2b15fe22d04da1372e2a20d Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Sun, 10 Mar 2024 21:05:50 -0400 Subject: [PATCH 33/61] Added actions kebab --- src/locales/messages.ts | 56 +++++------ .../components/dataToolbar/basicToolbar.tsx | 3 + .../dataToolbar/utils/bulkSelect.tsx | 13 ++- src/routes/settings/tagLabels/tagLabels.tsx | 20 ++-- .../components/actionsKebab/actionnsKebab.tsx | 62 ++++++++++++ .../components/actionsKebab/index.ts | 1 + .../components/deleteModal/deleteAction.tsx | 73 ++++++++++++++ .../components/deleteModal/deleteModal.tsx | 99 +++++++++++++++++++ .../components/deleteModal/index.ts | 2 + .../components/tagMappingModal/index.ts | 1 + .../settings/tagLabels/tagMapping/index.ts | 1 + src/routes/settings/tagLabels/tags/tags.tsx | 2 +- src/store/settings/settingsActions.ts | 2 +- src/store/settings/settingsReducer.ts | 17 ++-- 14 files changed, 298 insertions(+), 54 deletions(-) create mode 100644 src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx create mode 100644 src/routes/settings/tagLabels/tagMapping/components/actionsKebab/index.ts create mode 100644 src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx create mode 100644 src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx create mode 100644 src/routes/settings/tagLabels/tagMapping/components/deleteModal/index.ts create mode 100644 src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/index.ts create mode 100644 src/routes/settings/tagLabels/tagMapping/index.ts diff --git a/src/locales/messages.ts b/src/locales/messages.ts index bb25d466c..7c1dde43c 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -1436,12 +1436,12 @@ export default defineMessages({ 'https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-using-cost-models#understanding-cost-distribution_using-cost-models', id: 'docsPlatformProjects', }, - docsTagMappings: { + docsTagMapping: { defaultMessage: 'https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/index', description: 'https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/index', - id: 'docsTagMappings', + id: 'docsTagMapping', }, docsTags: { defaultMessage: @@ -3361,78 +3361,78 @@ export default defineMessages({ description: 'Tags and labels', id: 'tagLabels', }, - tagMappingsDelete: { + tagMappingDelete: { defaultMessage: 'Delete tag mapping', description: 'Delete tag mapping', - id: 'tagMappingsDelete', + id: 'tagMappingDelete', }, - tagMappingsDeleteDesc: { + tagMappingDeleteDesc: { defaultMessage: 'This action will remove the {value} tag mapping. Changes will be reflected within 24 hours.', description: 'This action will remove the {value} tag mapping. Changes will be reflected within 24 hours.', - id: 'tagMappingsDeleteDesc', + id: 'tagMappingDeleteDesc', }, - tagMappingsDesc: { + tagMappingDesc: { defaultMessage: 'Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}', description: 'Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}', - id: 'tagMappingsDesc', + id: 'tagMappingDesc', }, - tagMappingsWarning: { + tagMappingWarning: { defaultMessage: 'You must enable tags to use tag mapping.', description: 'You must enable tags to use tag mapping.', - id: 'tagMappingsWarning', + id: 'tagMappingWarning', }, - tagMappingsWizardDesc: { + tagMappingWizardDesc: { defaultMessage: 'Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours.', description: 'Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours.', - id: 'tagMappingsWizardDesc', + id: 'tagMappingWizardDesc', }, - tagMappingsWizardNavToCreateTagMapping: { + tagMappingWizardNavToCreateTagMapping: { defaultMessage: 'Create another tag mapping', description: 'Create another tag mapping', - id: 'tagMappingsWizardNavToCreateTagMapping', + id: 'tagMappingWizardNavToCreateTagMapping', }, - tagMappingsWizardNavToTagMappings: { + tagMappingWizardNavToTagMapping: { defaultMessage: 'Go back to Cost Management Settings', description: 'Go back to Cost Management Settings', - id: 'tagMappingsWizardNavToTagMappings', + id: 'tagMappingWizardNavToTagMapping', }, - tagMappingsWizardReview: { + tagMappingWizardReview: { defaultMessage: 'Review details', description: 'Review details', - id: 'tagMappingsWizardReview', + id: 'tagMappingWizardReview', }, - tagMappingsWizardReviewDesc: { + tagMappingWizardReviewDesc: { defaultMessage: 'Review and confirm the tag mappings. Click {create} to create the mappings, or {back} to revise. Changes to the reports will be reflected within 24 hours.', description: 'Review and confirm the tag mappings. Click {create} to create the mappings, or {back} to revise. Changes to the reports will be reflected within 24 hours.', - id: 'tagMappingsWizardReviewDesc', + id: 'tagMappingWizardReviewDesc', }, - tagMappingsWizardSelectChildTags: { + tagMappingWizardSelectChildTags: { defaultMessage: 'Select child tags', description: 'Select child tags', - id: 'tagMappingsWizardSelectChildTags', + id: 'tagMappingWizardSelectChildTags', }, - tagMappingsWizardSelectParentTag: { + tagMappingWizardSelectParentTag: { defaultMessage: 'Select parent tag', description: 'Select parent tag', - id: 'tagMappingsWizardSelectParentTag', + id: 'tagMappingWizardSelectParentTag', }, - tagMappingsWizardSuccess: { + tagMappingWizardSuccess: { defaultMessage: 'Tag mapping successful', description: 'Tag mapping successful', - id: 'tagMappingsWizardSuccess', + id: 'tagMappingWizardSuccess', }, - tagMappingsWizardSuccessDesc: { + tagMappingWizardSuccessDesc: { defaultMessage: 'Your tag keys were successfully mapped. Changes will be reflected in report summarizations within 24 hours.', description: 'Your tag keys were successfully mapped. Changes will be reflected in report summarizations within 24 hours.', - id: 'tagMappingsWizardSuccessDesc', + id: 'tagMappingWizardSuccessDesc', }, tagNames: { defaultMessage: 'Tag names', diff --git a/src/routes/components/dataToolbar/basicToolbar.tsx b/src/routes/components/dataToolbar/basicToolbar.tsx index f18c1abaa..ed24adf3b 100644 --- a/src/routes/components/dataToolbar/basicToolbar.tsx +++ b/src/routes/components/dataToolbar/basicToolbar.tsx @@ -48,6 +48,7 @@ interface BasicToolbarOwnProps { selectedItems?: any[]; showBulkSelect?: boolean; // Show bulk select showBulkSelectAll?: boolean; // Show bulk select all option + showBulkSelectPage?: boolean; // Show bulk select page option showFilter?: boolean; // Show export icon style?: React.CSSProperties; useActiveFilters?: boolean; @@ -136,6 +137,7 @@ export class BasicToolbarBase extends React.Component void; selectedItems?: ComputedReportItem[]; showSelectAll?: boolean; + showSelectPage?: boolean; }) => { const numSelected = isAllSelected ? itemsTotal : selectedItems ? selectedItems.length : 0; const allSelected = (isAllSelected || numSelected === itemsTotal) && itemsTotal > 0; @@ -43,11 +45,14 @@ export const getBulkSelect = ({ onBulkSelectClicked('none')}> {intl.formatMessage(messages.toolBarBulkSelectNone)} , - onBulkSelectClicked('page')}> - {intl.formatMessage(messages.toolBarBulkSelectPage, { value: itemsPerPage })} - , ]; - + if (showSelectPage) { + dropdownItems.push( + onBulkSelectClicked('page')}> + {intl.formatMessage(messages.toolBarBulkSelectPage, { value: itemsPerPage })} + + ); + } if (showSelectAll) { dropdownItems.push( onBulkSelectClicked('all')}> diff --git a/src/routes/settings/tagLabels/tagLabels.tsx b/src/routes/settings/tagLabels/tagLabels.tsx index 01bf9b329..0b2827036 100644 --- a/src/routes/settings/tagLabels/tagLabels.tsx +++ b/src/routes/settings/tagLabels/tagLabels.tsx @@ -5,19 +5,19 @@ import React, { useState } from 'react'; import { useIntl } from 'react-intl'; import { styles } from './tagLabels.styles'; -import { TagMappings } from './tagMappings'; +import TagMapping from './tagMapping/tagMapping'; import { Tags } from './tags'; // eslint-disable-next-line no-shadow const enum TagLabelsItem { - tagMappings = 'tagMappings', + tagMapping = 'tagMapping', tags = 'tags', } -export const getIdKeyForItem = (item: TagLabelsItem) => { +const getIdKeyForItem = (item: TagLabelsItem) => { switch (item) { - case TagLabelsItem.tagMappings: - return 'tagMappings'; + case TagLabelsItem.tagMapping: + return 'tagMapping'; case TagLabelsItem.tags: return 'tags'; } @@ -32,10 +32,6 @@ interface TagLabelsOwnProps { canWrite?: boolean; } -export interface TagLabelsStateProps { - isTagMappingToggleEnabled?: boolean; -} - type TagLabelsProps = TagLabelsOwnProps; const TagLabels: React.FC = ({ canWrite }) => { @@ -50,7 +46,7 @@ const TagLabels: React.FC = ({ canWrite }) => { label: intl.formatMessage(messages.tagLabelsEnable), }, { - item: TagLabelsItem.tagMappings, + item: TagLabelsItem.tagMapping, label: intl.formatMessage(messages.tagLabelsMap), }, ]; @@ -71,8 +67,8 @@ const TagLabels: React.FC = ({ canWrite }) => { } const currentItem = getIdKeyForItem(item); - if (currentItem === TagLabelsItem.tagMappings) { - return ; + if (currentItem === TagLabelsItem.tagMapping) { + return ; } else if (currentItem === TagLabelsItem.tags) { return ; } else { diff --git a/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx b/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx new file mode 100644 index 000000000..22a5ba053 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx @@ -0,0 +1,62 @@ +import type { SettingsData } from 'api/settings'; +import { SettingsType } from 'api/settings'; +import messages from 'locales/messages'; +import React, { useState } from 'react'; +import { useIntl } from 'react-intl'; +import type { DropdownWrapperItem } from 'routes/components/dropdownWrapper'; +import { DropdownWrapper } from 'routes/components/dropdownWrapper'; + +import { DeleteModal } from '../deleteModal'; + +interface ActionsKebabOwnProps { + canWrite?: boolean; + isDisabled?: boolean; + item: SettingsData; + onDelete(); +} + +type ActionsKebabProps = ActionsKebabOwnProps; + +const ActionnsKebab: React.FC = ({ canWrite, isDisabled, item, onDelete }) => { + const [isOpen, setIsOpen] = useState(false); + const intl = useIntl(); + + const getItems = () => { + const items: DropdownWrapperItem[] = [ + { + isDisabled: isDisabled || !canWrite, + onClick: handleOnClick, + toString: () => intl.formatMessage(messages.tagMappingDelete), + ...(!canWrite && { + tooltipProps: { + content:
{intl.formatMessage(messages.readOnlyPermissions)}
, + }, + }), + }, + ]; + return items; + }; + + const handleOnClose = () => { + setIsOpen(false); + }; + + const handleOnClick = () => { + setIsOpen(!isOpen); + }; + + return ( + <> + + + + ); +}; + +export default ActionnsKebab; diff --git a/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/index.ts b/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/index.ts new file mode 100644 index 000000000..85e7288ea --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/index.ts @@ -0,0 +1 @@ +export { default as ActionsKebab } from './actionnsKebab'; diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx new file mode 100644 index 000000000..014c43c14 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx @@ -0,0 +1,73 @@ +import './deleteModal.scss'; + +import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; +import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; +import type { SettingsData } from 'api/settings'; +import { SettingsType } from 'api/settings'; +import messages from 'locales/messages'; +import React, { useState } from 'react'; +import { useIntl } from 'react-intl'; + +import DeleteModal from './deleteModal'; + +interface DeleteActionOwnProps { + canWrite?: boolean; + isDisabled?: boolean; + item: SettingsData; + onDelete(); +} + +type DeleteActionProps = DeleteActionOwnProps; + +const DeleteAction: React.FC = ({ canWrite, isDisabled, item, onDelete }) => { + const [isOpen, setIsOpen] = useState(false); + const intl = useIntl(); + + const getActions = () => { + const getTooltip = children => { + if (!canWrite) { + const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); + return {children}; + } + return children; + }; + + return getTooltip( + + ); + }; + + const handleOnClose = () => { + setIsOpen(false); + }; + + const handleOnClick = () => { + setIsOpen(!isOpen); + }; + + // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. + // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree + + return ( + <> + {getActions()} + + + ); +}; + +export default DeleteAction; diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx new file mode 100644 index 000000000..a084f3b6a --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx @@ -0,0 +1,99 @@ +import './deleteModal.scss'; + +import { Button } from '@patternfly/react-core'; +import { Modal, ModalBody, ModalFooter, ModalHeader, ModalVariant } from '@patternfly/react-core/next'; +import type { SettingsData } from 'api/settings'; +import type { SettingsType } from 'api/settings'; +import type { AxiosError } from 'axios'; +import messages from 'locales/messages'; +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { settingsActions, settingsSelectors } from 'store/settings'; + +interface DeleteModalOwnProps { + isOpen?: boolean; + item: SettingsData; + onClose(); + onDelete(); + settingsType: SettingsType; +} + +interface DeleteModalMapProps { + settingsType: SettingsType; +} + +interface DeleteModalStateProps { + settingsUpdateError?: AxiosError; + settingsUpdateStatus?: FetchStatus; +} + +type DeleteModalProps = DeleteModalOwnProps; + +const DeleteModal: React.FC = ({ isOpen, item, onClose, onDelete, settingsType }) => { + const [isFinish, setIsFinish] = useState(false); + const { settingsUpdateError, settingsUpdateStatus } = useMapToProps({ settingsType }); + + const dispatch: ThunkDispatch = useDispatch(); + const intl = useIntl(); + + const handleOnDelete = () => { + if (settingsUpdateStatus !== FetchStatus.inProgress) { + setIsFinish(true); + dispatch( + settingsActions.updateSettings(settingsType, { + ids: [item.uuid], + }) + ); + } + }; + + useEffect(() => { + if (isFinish && settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { + onDelete(); + } + }, [isFinish, settingsUpdateError, settingsUpdateStatus]); + + // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. + // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree + + return ( + + + {intl.formatMessage(messages.tagMappingDeleteDesc, { value: {item.key} })} + + + + + + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = ({ settingsType }: DeleteModalMapProps): DeleteModalStateProps => { + const settingsUpdateStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, settingsType) + ); + const settingsUpdateError = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateError(state, settingsType) + ); + + return { + settingsUpdateError, + settingsUpdateStatus, + }; +}; + +export default DeleteModal; diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/index.ts b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/index.ts new file mode 100644 index 000000000..9a89892e6 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/index.ts @@ -0,0 +1,2 @@ +export { default as DeleteAction } from './deleteAction'; +export { default as DeleteModal } from './deleteModal'; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/index.ts b/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/index.ts new file mode 100644 index 000000000..142245871 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/index.ts @@ -0,0 +1 @@ +export { default as TagMappingModal } from './tagMappingModal'; diff --git a/src/routes/settings/tagLabels/tagMapping/index.ts b/src/routes/settings/tagLabels/tagMapping/index.ts new file mode 100644 index 000000000..892c8673e --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/index.ts @@ -0,0 +1 @@ +export { default as TagMapping } from './tagMapping'; diff --git a/src/routes/settings/tagLabels/tags/tags.tsx b/src/routes/settings/tagLabels/tags/tags.tsx index 6193555c0..482d99ed2 100644 --- a/src/routes/settings/tagLabels/tags/tags.tsx +++ b/src/routes/settings/tagLabels/tags/tags.tsx @@ -26,7 +26,7 @@ interface TagsOwnProps { canWrite?: boolean; } -export interface TagsMapProps { +interface TagsMapProps { query?: Query; } diff --git a/src/store/settings/settingsActions.ts b/src/store/settings/settingsActions.ts index 7a62f7112..79aa7c033 100644 --- a/src/store/settings/settingsActions.ts +++ b/src/store/settings/settingsActions.ts @@ -28,7 +28,7 @@ export const updateSettingsSuccess = createAction('settings/update/success')< >(); export const updateSettingsFailure = createAction('settings/update/failure')(); -export const resetSettingsState = createAction('settings/reset_state')(); +export const resetStatus = createAction('settings/status/reset')(); export function fetchSettings(settingsType: SettingsType, settingsQueryString: string): ThunkAction { return (dispatch, getState) => { diff --git a/src/store/settings/settingsReducer.ts b/src/store/settings/settingsReducer.ts index 161c86b7a..96b04cc0d 100644 --- a/src/store/settings/settingsReducer.ts +++ b/src/store/settings/settingsReducer.ts @@ -5,12 +5,7 @@ import { resetState } from 'store/ui/uiActions'; import type { ActionType } from 'typesafe-actions'; import { getType } from 'typesafe-actions'; -import { - resetSettingsState, - updateSettingsFailure, - updateSettingsRequest, - updateSettingsSuccess, -} from './settingsActions'; +import { resetStatus, updateSettingsFailure, updateSettingsRequest, updateSettingsSuccess } from './settingsActions'; import { fetchSettingsFailure, fetchSettingsRequest, fetchSettingsSuccess } from './settingsActions'; export type SettingsState = Readonly<{ @@ -32,17 +27,23 @@ export type SettingsAction = ActionType< | typeof updateSettingsFailure | typeof updateSettingsRequest | typeof updateSettingsSuccess - | typeof resetSettingsState | typeof resetState + | typeof resetStatus >; export function settingsReducer(state = defaultState, action: SettingsAction): SettingsState { switch (action.type) { case getType(resetState): - case getType(resetSettingsState): state = defaultState; return state; + case getType(resetStatus): + state = { + ...state, + status: new Map(), + }; + return state; + case getType(fetchSettingsFailure): return { ...state, From 5531684a0c895228f07b39d284cd5b61adc04262 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Sun, 10 Mar 2024 21:06:14 -0400 Subject: [PATCH 34/61] Renamed tagMappings* as tagMapping* --- .../components/childTags/childTags.tsx | 5 +- .../childTags/childTagsTable.styles.ts | 0 .../components/childTags/childTagsTable.tsx | 0 .../components/childTags/childTagsToolbar.tsx | 0 .../components/childTags/index.ts | 0 .../components/deleteModal/deleteModal.scss | 0 .../components/parentTags/index.ts | 0 .../components/parentTags/parentTags.tsx | 30 ++++- .../parentTags/parentTagsTable.styles.ts | 0 .../components/parentTags/parentTagsTable.tsx | 5 +- .../parentTags/parentTagsToolbar.tsx | 6 + .../tagMappingModal/tagMappingModal.tsx} | 10 +- .../components/tagMappingWizard/index.ts | 1 + .../tagMappingWizard.styles.ts} | 0 .../tagMappingWizard/tagMappingWizard.tsx} | 67 +++++---- .../tagMappingWizardEmptyState.tsx} | 23 ++-- .../tagMappingWizardReview.tsx} | 18 +-- .../tagMapping.styles.ts} | 0 .../tagMapping.tsx} | 30 ++--- .../tagMappingEmptyState.tsx} | 16 +-- .../tagMappingTable.tsx} | 25 ++-- .../tagMappingToolbar.tsx} | 13 +- .../components/deleteModal/deleteModal.tsx | 127 ------------------ .../components/deleteModal/index.ts | 1 - .../settings/tagLabels/tagMappings/index.ts | 1 - .../tagMappings/tagMappingsModal/index.ts | 1 - .../tagMappings/tagMappingsWizard/index.ts | 1 - 27 files changed, 150 insertions(+), 230 deletions(-) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/childTags/childTags.tsx (98%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/childTags/childTagsTable.styles.ts (100%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/childTags/childTagsTable.tsx (100%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/childTags/childTagsToolbar.tsx (100%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/childTags/index.ts (100%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/deleteModal/deleteModal.scss (100%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/parentTags/index.ts (100%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/parentTags/parentTags.tsx (89%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/parentTags/parentTagsTable.styles.ts (100%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/parentTags/parentTagsTable.tsx (91%) rename src/routes/settings/tagLabels/{tagMappings => tagMapping}/components/parentTags/parentTagsToolbar.tsx (93%) rename src/routes/settings/tagLabels/{tagMappings/tagMappingsModal/tagMappingsModal.tsx => tagMapping/components/tagMappingModal/tagMappingModal.tsx} (87%) create mode 100644 src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/index.ts rename src/routes/settings/tagLabels/{tagMappings/tagMappingsWizard/tagMappingsWizard.styles.ts => tagMapping/components/tagMappingWizard/tagMappingWizard.styles.ts} (100%) rename src/routes/settings/tagLabels/{tagMappings/tagMappingsWizard/tagMappingsWizard.tsx => tagMapping/components/tagMappingWizard/tagMappingWizard.tsx} (76%) rename src/routes/settings/tagLabels/{tagMappings/tagMappingsWizard/tagMappingsWizardEmptyState.tsx => tagMapping/components/tagMappingWizard/tagMappingWizardEmptyState.tsx} (64%) rename src/routes/settings/tagLabels/{tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx => tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx} (87%) rename src/routes/settings/tagLabels/{tagMappings/tagMappings.styles.ts => tagMapping/tagMapping.styles.ts} (100%) rename src/routes/settings/tagLabels/{tagMappings/tagMappings.tsx => tagMapping/tagMapping.tsx} (87%) rename src/routes/settings/tagLabels/{tagMappings/tagMappingsEmptyState.tsx => tagMapping/tagMappingEmptyState.tsx} (68%) rename src/routes/settings/tagLabels/{tagMappings/tagMappingsTable.tsx => tagMapping/tagMappingTable.tsx} (76%) rename src/routes/settings/tagLabels/{tagMappings/tagMappingsToolbar.tsx => tagMapping/tagMappingToolbar.tsx} (87%) delete mode 100644 src/routes/settings/tagLabels/tagMappings/components/deleteModal/deleteModal.tsx delete mode 100644 src/routes/settings/tagLabels/tagMappings/components/deleteModal/index.ts delete mode 100644 src/routes/settings/tagLabels/tagMappings/index.ts delete mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappingsModal/index.ts delete mode 100644 src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/index.ts diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx b/src/routes/settings/tagLabels/tagMapping/components/childTags/childTags.tsx similarity index 98% rename from src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx rename to src/routes/settings/tagLabels/tagMapping/components/childTags/childTags.tsx index e26089872..b82536730 100644 --- a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTags.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/childTags/childTags.tsx @@ -27,11 +27,11 @@ interface ChildTagsOwnProps { selectedItems?: SettingsData[]; } -export interface ChildTagsMapProps { +interface ChildTagsMapProps { query?: Query; } -export interface ChildTagsStateProps { +interface ChildTagsStateProps { settings?: Settings; settingsError?: AxiosError; settingsStatus?: FetchStatus; @@ -52,6 +52,7 @@ const baseQuery: Query = { const ChildTags: React.FC = ({ onBulkSelect, onSelect, selectedItems }: ChildTagsProps) => { const [query, setQuery] = useState({ ...baseQuery }); const { settings, settingsError, settingsStatus } = useMapToProps({ query }); + const intl = useIntl(); const getMappings = () => { diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.styles.ts b/src/routes/settings/tagLabels/tagMapping/components/childTags/childTagsTable.styles.ts similarity index 100% rename from src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.styles.ts rename to src/routes/settings/tagLabels/tagMapping/components/childTags/childTagsTable.styles.ts diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx b/src/routes/settings/tagLabels/tagMapping/components/childTags/childTagsTable.tsx similarity index 100% rename from src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsTable.tsx rename to src/routes/settings/tagLabels/tagMapping/components/childTags/childTagsTable.tsx diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsToolbar.tsx b/src/routes/settings/tagLabels/tagMapping/components/childTags/childTagsToolbar.tsx similarity index 100% rename from src/routes/settings/tagLabels/tagMappings/components/childTags/childTagsToolbar.tsx rename to src/routes/settings/tagLabels/tagMapping/components/childTags/childTagsToolbar.tsx diff --git a/src/routes/settings/tagLabels/tagMappings/components/childTags/index.ts b/src/routes/settings/tagLabels/tagMapping/components/childTags/index.ts similarity index 100% rename from src/routes/settings/tagLabels/tagMappings/components/childTags/index.ts rename to src/routes/settings/tagLabels/tagMapping/components/childTags/index.ts diff --git a/src/routes/settings/tagLabels/tagMappings/components/deleteModal/deleteModal.scss b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.scss similarity index 100% rename from src/routes/settings/tagLabels/tagMappings/components/deleteModal/deleteModal.scss rename to src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.scss diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/index.ts b/src/routes/settings/tagLabels/tagMapping/components/parentTags/index.ts similarity index 100% rename from src/routes/settings/tagLabels/tagMappings/components/parentTags/index.ts rename to src/routes/settings/tagLabels/tagMapping/components/parentTags/index.ts diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTags.tsx b/src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTags.tsx similarity index 89% rename from src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTags.tsx rename to src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTags.tsx index 8424b972f..d58a6d43e 100644 --- a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTags.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTags.tsx @@ -22,15 +22,17 @@ import { styles } from './parentTagsTable.styles'; import { ParentTagsToolbar } from './parentTagsToolbar'; interface ParentTagsOwnProps { + onBulkSelect(items: SettingsData[]); onSelect(items: SettingsData[], isSelected: boolean); selectedItems?: SettingsData[]; + unavailableItems?: SettingsData[]; } -export interface ParentTagsMapProps { +interface ParentTagsMapProps { query?: Query; } -export interface ParentTagsStateProps { +interface ParentTagsStateProps { settings?: Settings; settingsError?: AxiosError; settingsStatus?: FetchStatus; @@ -48,9 +50,15 @@ const baseQuery: Query = { }, }; -const ParentTags: React.FC = ({ onSelect, selectedItems }: ParentTagsProps) => { +const ParentTags: React.FC = ({ + onBulkSelect, + onSelect, + selectedItems, + unavailableItems, +}: ParentTagsProps) => { const [query, setQuery] = useState({ ...baseQuery }); const { settings, settingsError, settingsStatus } = useMapToProps({ query }); + const intl = useIntl(); const getMappings = () => { @@ -97,6 +105,7 @@ const ParentTags: React.FC = ({ onSelect, selectedItems }: Pare onSort={(sortType, isSortAscending) => handleOnSort(sortType, isSortAscending)} selectedItems={selectedItems} settings={settings} + unavailableItems={unavailableItems} /> ); }; @@ -109,6 +118,7 @@ const ParentTags: React.FC = ({ onSelect, selectedItems }: Pare isDisabled={mappings.length === 0} itemsPerPage={mappings.length} itemsTotal={itemsTotal} + onBulkSelect={handleOnBulkSelect} onFilterAdded={filter => handleOnFilterAdded(filter)} onFilterRemoved={filter => handleOnFilterRemoved(filter)} pagination={getPagination(isDisabled)} @@ -118,6 +128,20 @@ const ParentTags: React.FC = ({ onSelect, selectedItems }: Pare ); }; + const handleOnBulkSelect = (action: string) => { + if (action === 'none') { + onBulkSelect([]); + } else if (action === 'page') { + const newSelectedItems = [...selectedItems]; + getMappings().map(val => { + if (!newSelectedItems.find(item => item.uuid === val.uuid)) { + newSelectedItems.push(val); + } + }); + onBulkSelect(newSelectedItems); + } + }; + const handleOnFilterAdded = filter => { const newQuery = queryUtils.handleOnFilterAdded(query, filter); setQuery(newQuery); diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.styles.ts b/src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTagsTable.styles.ts similarity index 100% rename from src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.styles.ts rename to src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTagsTable.styles.ts diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx b/src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTagsTable.tsx similarity index 91% rename from src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx rename to src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTagsTable.tsx index 3ea2cb457..f2df28e42 100644 --- a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsTable.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTagsTable.tsx @@ -15,6 +15,7 @@ interface ParentTagsTableOwnProps { orderBy?: any; selectedItems?: SettingsData[]; settings: Settings; + unavailableItems?: SettingsData[]; } type ParentTagsTableProps = ParentTagsTableOwnProps; @@ -27,6 +28,7 @@ const ParentTagsTable: React.FC = ({ orderBy, selectedItems, settings, + unavailableItems, }) => { const [columns, setColumns] = useState([]); const [rows, setRows] = useState([]); @@ -71,7 +73,8 @@ const ParentTagsTable: React.FC = ({ }, ], item, - selected: selectedItems && selectedItems.find(val => val.uuid === item.uuid) !== undefined, + selected: selectedItems?.find(val => val.uuid === item.uuid) !== undefined, + selectionDisabled: unavailableItems?.find(val => val.uuid === item.uuid), }); }); diff --git a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsToolbar.tsx b/src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTagsToolbar.tsx similarity index 93% rename from src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsToolbar.tsx rename to src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTagsToolbar.tsx index 3da8e3598..53d308eda 100644 --- a/src/routes/settings/tagLabels/tagMappings/components/parentTags/parentTagsToolbar.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/parentTags/parentTagsToolbar.tsx @@ -11,6 +11,7 @@ interface ParentTagsToolbarOwnProps { isDisabled?: boolean; itemsPerPage?: number; itemsTotal?: number; + onBulkSelect(action: string); onFilterAdded(filter: Filter); onFilterRemoved(filter: Filter); pagination?: React.ReactNode; @@ -24,6 +25,7 @@ const ParentTagsToolbar: React.FC = ({ isDisabled, itemsPerPage, itemsTotal, + onBulkSelect, onFilterAdded, onFilterRemoved, pagination, @@ -81,12 +83,16 @@ const ParentTagsToolbar: React.FC = ({ isDisabled={isDisabled} itemsPerPage={itemsPerPage} itemsTotal={itemsTotal} + onBulkSelect={onBulkSelect} onFilterAdded={onFilterAdded} onFilterRemoved={onFilterRemoved} pagination={pagination} query={query} selectedItems={selectedItems} + showBulkSelect showFilter + showBulkSelectAll={false} + showBulkSelectPage={false} /> ); }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/tagMappingsModal.tsx b/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx similarity index 87% rename from src/routes/settings/tagLabels/tagMappings/tagMappingsModal/tagMappingsModal.tsx rename to src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx index 201aaf188..0e0e24116 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/tagMappingsModal.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx @@ -4,17 +4,17 @@ import type { SettingsData } from 'api/settings'; import messages from 'locales/messages'; import React, { useState } from 'react'; import { useIntl } from 'react-intl'; -import { ChildTags } from 'routes/settings/tagLabels/tagMappings/components/childTags'; +import { ChildTags } from 'routes/settings/tagLabels/tagMapping/components/childTags'; import { useStateCallback } from 'utils/hooks'; -interface tagMappingsModalOwnProps { +interface TagMappingModalOwnProps { canWrite?: boolean; isDisabled?: boolean; } -type tagMappingsModalProps = tagMappingsModalOwnProps; +type TagMappingModalProps = TagMappingModalOwnProps; -const TagMappingsModal: React.FC = ({ canWrite, isDisabled }: tagMappingsModalProps) => { +const TagMappingModal: React.FC = ({ canWrite, isDisabled }: TagMappingModalProps) => { const [isOpen, setIsOpen] = useState(false); const [selectedItems, setSelectedItems] = useStateCallback([]); @@ -82,4 +82,4 @@ const TagMappingsModal: React.FC = ({ canWrite, isDisable ); }; -export default TagMappingsModal; +export default TagMappingModal; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/index.ts b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/index.ts new file mode 100644 index 000000000..e8b6e2c9d --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/index.ts @@ -0,0 +1 @@ +export { default as TagMappingWizard } from './tagMappingWizard'; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.styles.ts b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.styles.ts similarity index 100% rename from src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.styles.ts rename to src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.styles.ts diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx similarity index 76% rename from src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx rename to src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx index 0b1c19d38..c204890d8 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizard.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx @@ -9,34 +9,34 @@ import { useIntl } from 'react-intl'; import { useDispatch, useSelector } from 'react-redux'; import type { AnyAction } from 'redux'; import type { ThunkDispatch } from 'redux-thunk'; -import { ChildTags } from 'routes/settings/tagLabels/tagMappings/components/childTags'; -import { ParentTags } from 'routes/settings/tagLabels/tagMappings/components/parentTags'; +import { ChildTags } from 'routes/settings/tagLabels/tagMapping/components/childTags'; +import { ParentTags } from 'routes/settings/tagLabels/tagMapping/components/parentTags'; import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; import { settingsActions, settingsSelectors } from 'store/settings'; -import { styles } from './tagMappingsWizard.styles'; -import { TagMappingsEmptyState } from './tagMappingsWizardEmptyState'; -import { TagMappingsWizardReview } from './tagMappingsWizardReview'; +import { styles } from './tagMappingWizard.styles'; +import { TagMappingEmptyState } from './tagMappingWizardEmptyState'; +import { TagMappingWizardReview } from './tagMappingWizardReview'; -interface TagMappingsWizardOwnProps { +interface TagMappingWizardOwnProps { canWrite?: boolean; isDisabled?: boolean; onClose(); } -interface TagMappingsWizardStateProps { +interface TagMappingWizardStateProps { settingsUpdateError?: AxiosError; settingsUpdateStatus?: FetchStatus; } -type TagMappingsWizardProps = TagMappingsWizardOwnProps; +type TagMappingWizardProps = TagMappingWizardOwnProps; -const TagMappingsWizard: React.FC = ({ +const TagMappingWizard: React.FC = ({ canWrite, isDisabled, onClose, -}: TagMappingsWizardProps) => { +}: TagMappingWizardProps) => { const [childTags, setChildTags] = useState([]); const [isFinished, setIsFinished] = useState(false); const [isOpen, setIsOpen] = useState(false); @@ -68,12 +68,12 @@ const TagMappingsWizard: React.FC = ({
- +
@@ -89,7 +89,7 @@ const TagMappingsWizard: React.FC = ({ @@ -102,9 +102,13 @@ const TagMappingsWizard: React.FC = ({ isNextDisabled: childTags.length === 0, }} id="step-1" - name={intl.formatMessage(messages.tagMappingsWizardSelectChildTags)} + name={intl.formatMessage(messages.tagMappingWizardSelectChildTags)} > - + = ({ }} id="step-2" isDisabled={childTags.length === 0} - name={intl.formatMessage(messages.tagMappingsWizardSelectParentTag)} + name={intl.formatMessage(messages.tagMappingWizardSelectParentTag)} > - + = ({ }} id="step-3" isDisabled={childTags.length === 0} - name={intl.formatMessage(messages.tagMappingsWizardReview)} + name={intl.formatMessage(messages.tagMappingWizardReview)} > - = ({ ); }; - const handleOnBulkSelect = (items: SettingsData[]) => { + const handleOnBulkSelectChild = (items: SettingsData[]) => { setChildTags(items); }; + const handleOnBulkSelectParent = (items: SettingsData[]) => { + setParentTags(items); + }; + const handleOnClick = () => { setIsOpen(!isOpen); }; @@ -168,7 +181,6 @@ const TagMappingsWizard: React.FC = ({ setChildTags([]); setParentTags([]); setIsFinished(false); - dispatch(settingsActions.resetSettingsState()); }; const handleOnSelectChild = (items: SettingsData[], isSelected: boolean = false) => { @@ -200,11 +212,18 @@ const TagMappingsWizard: React.FC = ({ }; useEffect(() => { - if (settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { + if (isOpen && settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { setIsFinished(true); } }, [settingsUpdateError, settingsUpdateStatus]); + // Clear error state if tags changed + useEffect(() => { + if (settingsUpdateError) { + dispatch(settingsActions.resetStatus()); + } + }, [childTags, parentTags]); + return ( <> {getActions()} @@ -213,7 +232,7 @@ const TagMappingsWizard: React.FC = ({ ); }; -const useMapToProps = (): TagMappingsWizardStateProps => { +const useMapToProps = (): TagMappingWizardStateProps => { const settingsUpdateStatus = useSelector((state: RootState) => settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildAdd) ); @@ -227,4 +246,4 @@ const useMapToProps = (): TagMappingsWizardStateProps => { }; }; -export default TagMappingsWizard; +export default TagMappingWizard; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardEmptyState.tsx b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardEmptyState.tsx similarity index 64% rename from src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardEmptyState.tsx rename to src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardEmptyState.tsx index bc43c5807..1f004491c 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardEmptyState.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardEmptyState.tsx @@ -14,25 +14,22 @@ import messages from 'locales/messages'; import React from 'react'; import { useIntl } from 'react-intl'; -import { styles } from './tagMappingsWizard.styles'; +import { styles } from './tagMappingWizard.styles'; -interface TagMappingsEmptyStateOwnProps { +interface TagMappingEmptyStateOwnProps { onClose(event); onReset(event); } -type TagMappingsEmptyStateProps = TagMappingsEmptyStateOwnProps; +type TagMappingEmptyStateProps = TagMappingEmptyStateOwnProps; -const TagMappingsEmptyState: React.FC = ({ - onClose, - onReset, -}: TagMappingsEmptyStateProps) => { +const TagMappingEmptyState: React.FC = ({ onClose, onReset }: TagMappingEmptyStateProps) => { const intl = useIntl(); return ( @@ -41,9 +38,9 @@ const TagMappingsEmptyState: React.FC = ({ headingLevel="h5" /> - {intl.formatMessage(messages.tagMappingsWizardSuccessDesc, { + {intl.formatMessage(messages.tagMappingWizardSuccessDesc, { learnMore: ( - + {intl.formatMessage(messages.learnMore)} ), @@ -53,12 +50,12 @@ const TagMappingsEmptyState: React.FC = ({
@@ -66,4 +63,4 @@ const TagMappingsEmptyState: React.FC = ({ ); }; -export { TagMappingsEmptyState }; +export { TagMappingEmptyState }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx similarity index 87% rename from src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx rename to src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx index ded950b5f..7bfcb0452 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/tagMappingsWizardReview.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx @@ -19,23 +19,23 @@ import React from 'react'; import { useIntl } from 'react-intl'; import { FetchStatus } from 'store/common'; -import { styles } from './tagMappingsWizard.styles'; +import { styles } from './tagMappingWizard.styles'; -interface tagMappingsWizardReviewOwnProps { +interface TagMappingWizardReviewOwnProps { childTags?: SettingsData[]; parentTags?: SettingsData[]; settingsError?: AxiosError; settingsStatus?: FetchStatus; } -type tagMappingsWizardReviewProps = tagMappingsWizardReviewOwnProps; +type TagMappingWizardReviewProps = TagMappingWizardReviewOwnProps; -const TagMappingsWizardReview: React.FC = ({ +const TagMappingWizardReview: React.FC = ({ childTags = [], parentTags = [], settingsError, settingsStatus, -}: tagMappingsWizardReviewProps) => { +}: TagMappingWizardReviewProps) => { const intl = useIntl(); const parseApiError = error => { @@ -54,19 +54,19 @@ const TagMappingsWizardReview: React.FC = ({ return ( <> - {settingsStatus !== FetchStatus.inProgress && settingsError && ( + {settingsStatus === FetchStatus.complete && settingsError && ( )} - {intl.formatMessage(messages.tagMappingsWizardReview)} + {intl.formatMessage(messages.tagMappingWizardReview)} - {intl.formatMessage(messages.tagMappingsWizardReviewDesc, { + {intl.formatMessage(messages.tagMappingWizardReviewDesc, { create: {intl.formatMessage(messages.create)}, back: {intl.formatMessage(messages.back)}, })} @@ -123,4 +123,4 @@ const TagMappingsWizardReview: React.FC = ({ ); }; -export { TagMappingsWizardReview }; +export { TagMappingWizardReview }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts b/src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts similarity index 100% rename from src/routes/settings/tagLabels/tagMappings/tagMappings.styles.ts rename to src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx b/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx similarity index 87% rename from src/routes/settings/tagLabels/tagMappings/tagMappings.tsx rename to src/routes/settings/tagLabels/tagMapping/tagMapping.tsx index 249a27ca4..cf5f3e0c7 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappings.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx @@ -17,20 +17,20 @@ import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; import { settingsActions, settingsSelectors } from 'store/settings'; -import { styles } from './tagMappings.styles'; -import { TagMappingsEmptyState } from './tagMappingsEmptyState'; -import { TagMappingsTable } from './tagMappingsTable'; -import { TagMappingsToolbar } from './tagMappingsToolbar'; +import { styles } from './tagMapping.styles'; +import { TagMappingEmptyState } from './tagMappingEmptyState'; +import { TagMappingTable } from './tagMappingTable'; +import { TagMappingToolbar } from './tagMappingToolbar'; interface MappingsOwnProps { canWrite?: boolean; } -export interface MappingsMapProps { +interface MappingsMapProps { query?: Query; } -export interface MappingsStateProps { +interface MappingsStateProps { settings?: Settings; settingsError?: AxiosError; settingsStatus?: FetchStatus; @@ -48,13 +48,12 @@ const baseQuery: Query = { }, }; -const TagMappings: React.FC = ({ canWrite }) => { +const TagMapping: React.FC = ({ canWrite }) => { const [query, setQuery] = useState({ ...baseQuery }); const { settings, settingsError, settingsStatus } = useMapToProps({ query, }); - const dispatch: ThunkDispatch = useDispatch(); const intl = useIntl(); const getMappings = () => { @@ -93,7 +92,7 @@ const TagMappings: React.FC = ({ canWrite }) => { const getTable = () => { return ( - = ({ canWrite }) => { const itemsTotal = settings?.meta ? settings.meta.count : 0; return ( - = ({ canWrite }) => { }; const handleOnDelete = () => { - dispatch(settingsActions.resetSettingsState()); refresh(); }; @@ -174,13 +172,13 @@ const TagMappings: React.FC = ({ canWrite }) => { return ( <>
- {intl.formatMessage(messages.tagMappingsDesc, { + {intl.formatMessage(messages.tagMappingDesc, { learnMore: ( - + {intl.formatMessage(messages.learnMore)} ), - warning: {intl.formatMessage(messages.tagMappingsWarning)}, + warning: {intl.formatMessage(messages.tagMappingWarning)}, })}
{hasMappings && getToolbar(mappings)} @@ -193,7 +191,7 @@ const TagMappings: React.FC = ({ canWrite }) => { ) : (
- +
)} @@ -235,4 +233,4 @@ const useMapToProps = ({ query }: MappingsMapProps): MappingsStateProps => { }; }; -export default TagMappings; +export default TagMapping; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingEmptyState.tsx similarity index 68% rename from src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx rename to src/routes/settings/tagLabels/tagMapping/tagMappingEmptyState.tsx index 00cd5ac50..c1e427f82 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsEmptyState.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingEmptyState.tsx @@ -11,21 +11,21 @@ import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circ import messages from 'locales/messages'; import React from 'react'; import { useIntl } from 'react-intl'; -import { TagMappingsWizard } from 'routes/settings/tagLabels/tagMappings/tagMappingsWizard'; +import { TagMappingWizard } from 'routes/settings/tagLabels/tagMapping/components/tagMappingWizard'; -interface TagMappingsEmptyStateOwnProps { +interface TagMappingEmptyStateOwnProps { canWrite?: boolean; isDisabled?: boolean; onWizardClose(); } -type TagMappingsEmptyStateProps = TagMappingsEmptyStateOwnProps; +type TagMappingEmptyStateProps = TagMappingEmptyStateOwnProps; -const TagMappingsEmptyState: React.FC = ({ +const TagMappingEmptyState: React.FC = ({ canWrite, isDisabled, onWizardClose, -}: TagMappingsEmptyStateOwnProps) => { +}: TagMappingEmptyStateOwnProps) => { const intl = useIntl(); return ( @@ -38,7 +38,7 @@ const TagMappingsEmptyState: React.FC = ({ {intl.formatMessage(messages.noMappedTagsDesc, { learnMore: ( - + {intl.formatMessage(messages.learnMore)} ), @@ -47,11 +47,11 @@ const TagMappingsEmptyState: React.FC = ({ - +
); }; -export { TagMappingsEmptyState }; +export { TagMappingEmptyState }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx similarity index 76% rename from src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx rename to src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx index 765fad0f7..a9e3303da 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsTable.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx @@ -5,11 +5,12 @@ import messages from 'locales/messages'; import React, { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; import { ExpandableTable } from 'routes/components/dataTable'; +import { ActionsKebab } from 'routes/settings/tagLabels/tagMapping/components/actionsKebab'; +import { DeleteAction } from 'routes/settings/tagLabels/tagMapping/components/deleteModal'; -import DeleteModal from './components/deleteModal/deleteModal'; -import { styles } from './tagMappings.styles'; +import { styles } from './tagMapping.styles'; -interface TagMappingsTableOwnProps { +interface TagMappingTableOwnProps { canWrite?: boolean; filterBy?: any; isDisabled?: boolean; @@ -20,9 +21,9 @@ interface TagMappingsTableOwnProps { settings: Settings; } -type TagMappingsTableProps = TagMappingsTableOwnProps; +type TagMappingTableProps = TagMappingTableOwnProps; -const TagMappingsTable: React.FC = ({ +const TagMappingTable: React.FC = ({ canWrite, filterBy, isDisabled, @@ -43,7 +44,7 @@ const TagMappingsTable: React.FC = ({ } const newRows = []; - const tagMappings = settings?.data ? (settings.data as any) : []; + const tagMapping = settings?.data ? (settings.data as any) : []; const newColumns = [ { @@ -52,19 +53,19 @@ const TagMappingsTable: React.FC = ({ { orderBy: 'parent', name: intl.formatMessage(messages.detailsResourceNames, { value: 'tag_key' }), - ...(tagMappings.length && { isSortable: true }), + ...(tagMapping.length && { isSortable: true }), }, { orderBy: 'source_type', name: intl.formatMessage(messages.sourceType), - ...(tagMappings.length && { isSortable: true }), + ...(tagMapping.length && { isSortable: true }), }, { name: '', }, ]; - tagMappings.map(item => { + tagMapping.map(item => { const parent = item.parent; newRows.push({ cells: [ @@ -76,7 +77,7 @@ const TagMappingsTable: React.FC = ({ value: intl.formatMessage(messages.sourceTypes, { value: parent?.source_type?.toLowerCase() }), }, { - value: 'Test...', + value: , }, ], children: parent.children.map(child => { @@ -92,7 +93,7 @@ const TagMappingsTable: React.FC = ({ style: styles.expandableRowContent, }, { - value: , + value: , }, ], item: child, @@ -130,4 +131,4 @@ const TagMappingsTable: React.FC = ({ ); }; -export { TagMappingsTable }; +export { TagMappingTable }; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx similarity index 87% rename from src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx rename to src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx index b2ddb8f6d..9f6633782 100644 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsToolbar.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx @@ -4,9 +4,10 @@ import React from 'react'; import { useIntl } from 'react-intl'; import { BasicToolbar } from 'routes/components/dataToolbar'; import type { ToolbarChipGroupExt } from 'routes/components/dataToolbar/utils/common'; -import { TagMappingsWizard } from 'routes/settings/tagLabels/tagMappings/tagMappingsWizard'; +import { TagMappingWizard } from 'routes/settings/tagLabels/tagMapping/components/tagMappingWizard'; import type { Filter } from 'routes/utils/filter'; -interface TagMappingsToolbarOwnProps { + +interface TagMappingToolbarOwnProps { canWrite?: boolean; isAllSelected?: boolean; isDisabled?: boolean; @@ -19,9 +20,9 @@ interface TagMappingsToolbarOwnProps { query?: Query; } -type TagMappingsToolbarProps = TagMappingsToolbarOwnProps; +type TagMappingToolbarProps = TagMappingToolbarOwnProps; -const TagMappingsToolbar: React.FC = ({ +const TagMappingToolbar: React.FC = ({ canWrite, isAllSelected, isDisabled, @@ -86,7 +87,7 @@ const TagMappingsToolbar: React.FC = ({ return ( } + actions={} categoryOptions={getCategoryOptions()} isAllSelected={isAllSelected} isDisabled={isDisabled} @@ -102,4 +103,4 @@ const TagMappingsToolbar: React.FC = ({ ); }; -export { TagMappingsToolbar }; +export { TagMappingToolbar }; diff --git a/src/routes/settings/tagLabels/tagMappings/components/deleteModal/deleteModal.tsx b/src/routes/settings/tagLabels/tagMappings/components/deleteModal/deleteModal.tsx deleted file mode 100644 index ceb10d34c..000000000 --- a/src/routes/settings/tagLabels/tagMappings/components/deleteModal/deleteModal.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import './deleteModal.scss'; - -import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; -import { Modal, ModalBody, ModalFooter, ModalHeader, ModalVariant } from '@patternfly/react-core/next'; -import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; -import type { SettingsData } from 'api/settings'; -import { SettingsType } from 'api/settings'; -import type { AxiosError } from 'axios'; -import messages from 'locales/messages'; -import React, { useEffect, useState } from 'react'; -import { useIntl } from 'react-intl'; -import { useDispatch, useSelector } from 'react-redux'; -import type { AnyAction } from 'redux'; -import type { ThunkDispatch } from 'redux-thunk'; -import type { RootState } from 'store'; -import { FetchStatus } from 'store/common'; -import { settingsActions, settingsSelectors } from 'store/settings'; - -interface deleteModalOwnProps { - canWrite?: boolean; - isDisabled?: boolean; - item: SettingsData; - onDelete(); -} - -export interface DeleteModalStateProps { - settingsUpdateError?: AxiosError; - settingsUpdateStatus?: FetchStatus; -} - -type DeleteModalProps = deleteModalOwnProps; - -const DeleteModal: React.FC = ({ canWrite, isDisabled, item, onDelete }) => { - const [isOpen, setIsOpen] = useState(false); - const { settingsUpdateError, settingsUpdateStatus } = useMapToProps(); - - const dispatch: ThunkDispatch = useDispatch(); - const intl = useIntl(); - - const getActions = () => { - const getTooltip = children => { - if (!canWrite) { - const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); - return {children}; - } - return children; - }; - - return getTooltip( - - ); - }; - - const handleOnDelete = () => { - if (settingsUpdateStatus !== FetchStatus.inProgress) { - dispatch( - settingsActions.updateSettings(SettingsType.tagsMappingsChildRemove, { - ids: [item.uuid], - }) - ); - } - }; - - const handleOnClose = () => { - setIsOpen(false); - }; - - const handleOnClick = () => { - setIsOpen(!isOpen); - }; - - useEffect(() => { - if (settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { - onDelete(); - } - }, [settingsUpdateError, settingsUpdateStatus]); - - // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. - // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree - - return ( - <> - {getActions()} - - - {intl.formatMessage(messages.tagMappingsDeleteDesc, { value: {item.key} })} - - - - - - - ); -}; - -// eslint-disable-next-line no-empty-pattern -const useMapToProps = (): DeleteModalStateProps => { - const settingsUpdateStatus = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildRemove) - ); - const settingsUpdateError = useSelector((state: RootState) => - settingsSelectors.selectSettingsUpdateError(state, SettingsType.tagsMappingsChildRemove) - ); - - return { - settingsUpdateError, - settingsUpdateStatus, - }; -}; - -export default DeleteModal; diff --git a/src/routes/settings/tagLabels/tagMappings/components/deleteModal/index.ts b/src/routes/settings/tagLabels/tagMappings/components/deleteModal/index.ts deleted file mode 100644 index 3175295fc..000000000 --- a/src/routes/settings/tagLabels/tagMappings/components/deleteModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as TagMappingsModal } from './deleteModal'; diff --git a/src/routes/settings/tagLabels/tagMappings/index.ts b/src/routes/settings/tagLabels/tagMappings/index.ts deleted file mode 100644 index e9d994c6d..000000000 --- a/src/routes/settings/tagLabels/tagMappings/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as TagMappings } from './tagMappings'; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/index.ts b/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/index.ts deleted file mode 100644 index 0d97b0765..000000000 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as TagMappingsModal } from './tagMappingsModal'; diff --git a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/index.ts b/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/index.ts deleted file mode 100644 index fed4df454..000000000 --- a/src/routes/settings/tagLabels/tagMappings/tagMappingsWizard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as TagMappingsWizard } from './tagMappingsWizard'; From 9a40f013ffe75f71b563602a865c4a7d20f79c83 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Sun, 10 Mar 2024 22:28:13 -0400 Subject: [PATCH 35/61] Created "Add child tags" tag mapping modal --- locales/data.json | 48 +++++-- locales/translations.json | 30 ++-- src/locales/messages.ts | 12 ++ .../components/actionsKebab/actionnsKebab.tsx | 48 +++++-- .../components/deleteModal/deleteAction.tsx | 6 +- .../components/deleteModal/deleteModal.tsx | 6 +- .../tagMappingModal/tagMappingModal.styles.ts | 8 ++ .../tagMappingModal/tagMappingModal.tsx | 133 +++++++++++------- .../tagMappingWizard/tagMappingWizard.tsx | 8 +- .../tagMappingWizardReview.tsx | 15 +- .../tagLabels/tagMapping/tagMapping.tsx | 10 +- .../tagLabels/tagMapping/tagMappingTable.tsx | 8 +- .../tagMapping/utils/parseApiError.ts | 13 ++ 13 files changed, 226 insertions(+), 119 deletions(-) create mode 100644 src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.styles.ts create mode 100644 src/routes/settings/tagLabels/tagMapping/utils/parseApiError.ts diff --git a/locales/data.json b/locales/data.json index 92fa900b4..01ecc1e3e 100644 --- a/locales/data.json +++ b/locales/data.json @@ -3544,7 +3544,7 @@ "value": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-using-cost-models#understanding-cost-distribution_using-cost-models" } ], - "docsTagMappings": [ + "docsTagMapping": [ { "type": 0, "value": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/index" @@ -12108,13 +12108,33 @@ "value": "Map tags and labels" } ], - "tagMappingsDelete": [ + "tagMappingAddChildTags": [ + { + "type": 0, + "value": "Add child tags" + } + ], + "tagMappingAddChildTagsDesc": [ + { + "type": 0, + "value": "Select additional tag key(s) that will be mapped to the " + }, + { + "type": 1, + "value": "value" + }, + { + "type": 0, + "value": " tag map. Tags that have been already mapped will not be available for selection." + } + ], + "tagMappingDelete": [ { "type": 0, "value": "Delete tag mapping" } ], - "tagMappingsDeleteDesc": [ + "tagMappingDeleteDesc": [ { "type": 0, "value": "This action will remove the " @@ -12128,7 +12148,7 @@ "value": " tag mapping. Changes will be reflected within 24 hours." } ], - "tagMappingsDesc": [ + "tagMappingDesc": [ { "type": 0, "value": "Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. " @@ -12146,37 +12166,37 @@ "value": "learnMore" } ], - "tagMappingsWarning": [ + "tagMappingWarning": [ { "type": 0, "value": "You must enable tags to use tag mapping." } ], - "tagMappingsWizardDesc": [ + "tagMappingWizardDesc": [ { "type": 0, "value": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours." } ], - "tagMappingsWizardNavToCreateTagMapping": [ + "tagMappingWizardNavToCreateTagMapping": [ { "type": 0, "value": "Create another tag mapping" } ], - "tagMappingsWizardNavToTagMappings": [ + "tagMappingWizardNavToTagMapping": [ { "type": 0, "value": "Go back to Cost Management Settings" } ], - "tagMappingsWizardReview": [ + "tagMappingWizardReview": [ { "type": 0, "value": "Review details" } ], - "tagMappingsWizardReviewDesc": [ + "tagMappingWizardReviewDesc": [ { "type": 0, "value": "Review and confirm the tag mappings. Click " @@ -12198,25 +12218,25 @@ "value": " to revise. Changes to the reports will be reflected within 24 hours." } ], - "tagMappingsWizardSelectChildTags": [ + "tagMappingWizardSelectChildTags": [ { "type": 0, "value": "Select child tags" } ], - "tagMappingsWizardSelectParentTag": [ + "tagMappingWizardSelectParentTag": [ { "type": 0, "value": "Select parent tag" } ], - "tagMappingsWizardSuccess": [ + "tagMappingWizardSuccess": [ { "type": 0, "value": "Tag mapping successful" } ], - "tagMappingsWizardSuccessDesc": [ + "tagMappingWizardSuccessDesc": [ { "type": 0, "value": "Your tag keys were successfully mapped. Changes will be reflected in report summarizations within 24 hours." diff --git a/locales/translations.json b/locales/translations.json index 6863a3ae6..be5123b5c 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -241,7 +241,7 @@ "docsCostModelsMarkup": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-setting-up-cost-models#creating-an-AWS-Azure-cost-model_setting-up-cost-models", "docsCostModelsOcp": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-setting-up-cost-models#creating-an-ocp-cost-model_setting-up-cost-models", "docsPlatformProjects": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/using_cost_models/assembly-using-cost-models#understanding-cost-distribution_using-cost-models", - "docsTagMappings": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/index", + "docsTagMapping": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/index", "docsTags": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html/managing_cost_data_using_tagging/assembly-configuring-tags-and-labels-in-cost-management", "docsUsingCostModels": "https://access.redhat.com/documentation/en-us/cost_management_service/1-latest/html-single/using_cost_models/index", "download": "Download", @@ -545,19 +545,21 @@ "tagLabels": "Tags and labels", "tagLabelsEnable": "Enable tags and labels", "tagLabelsMap": "Map tags and labels", - "tagMappingsDelete": "Delete tag mapping", - "tagMappingsDeleteDesc": "This action will remove the {value} tag mapping. Changes will be reflected within 24 hours.", - "tagMappingsDesc": "Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}", - "tagMappingsWarning": "You must enable tags to use tag mapping.", - "tagMappingsWizardDesc": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours.", - "tagMappingsWizardNavToCreateTagMapping": "Create another tag mapping", - "tagMappingsWizardNavToTagMappings": "Go back to Cost Management Settings", - "tagMappingsWizardReview": "Review details", - "tagMappingsWizardReviewDesc": "Review and confirm the tag mappings. Click {create} to create the mappings, or {back} to revise. Changes to the reports will be reflected within 24 hours.", - "tagMappingsWizardSelectChildTags": "Select child tags", - "tagMappingsWizardSelectParentTag": "Select parent tag", - "tagMappingsWizardSuccess": "Tag mapping successful", - "tagMappingsWizardSuccessDesc": "Your tag keys were successfully mapped. Changes will be reflected in report summarizations within 24 hours.", + "tagMappingAddChildTags": "Add child tags", + "tagMappingAddChildTagsDesc": "Select additional tag key(s) that will be mapped to the {value} tag map. Tags that have been already mapped will not be available for selection.", + "tagMappingDelete": "Delete tag mapping", + "tagMappingDeleteDesc": "This action will remove the {value} tag mapping. Changes will be reflected within 24 hours.", + "tagMappingDesc": "Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}", + "tagMappingWarning": "You must enable tags to use tag mapping.", + "tagMappingWizardDesc": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours.", + "tagMappingWizardNavToCreateTagMapping": "Create another tag mapping", + "tagMappingWizardNavToTagMapping": "Go back to Cost Management Settings", + "tagMappingWizardReview": "Review details", + "tagMappingWizardReviewDesc": "Review and confirm the tag mappings. Click {create} to create the mappings, or {back} to revise. Changes to the reports will be reflected within 24 hours.", + "tagMappingWizardSelectChildTags": "Select child tags", + "tagMappingWizardSelectParentTag": "Select parent tag", + "tagMappingWizardSuccess": "Tag mapping successful", + "tagMappingWizardSuccessDesc": "Your tag keys were successfully mapped. Changes will be reflected in report summarizations within 24 hours.", "tagNames": "Tag names", "timeOfExport": "Time of export", "to": "to", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 7c1dde43c..4d3a2f6e7 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -3361,6 +3361,18 @@ export default defineMessages({ description: 'Tags and labels', id: 'tagLabels', }, + tagMappingAddChildTags: { + defaultMessage: 'Add child tags', + description: 'Add child tags', + id: 'tagMappingAddChildTags', + }, + tagMappingAddChildTagsDesc: { + defaultMessage: + 'Select additional tag key(s) that will be mapped to the {value} tag map. Tags that have been already mapped will not be available for selection.', + description: + 'Select additional tag key(s) that will be mapped to the {value} tag map. Tags that have been already mapped will not be available for selection.', + id: 'tagMappingAddChildTagsDesc', + }, tagMappingDelete: { defaultMessage: 'Delete tag mapping', description: 'Delete tag mapping', diff --git a/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx b/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx index 22a5ba053..a866ec8e5 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx @@ -7,25 +7,37 @@ import type { DropdownWrapperItem } from 'routes/components/dropdownWrapper'; import { DropdownWrapper } from 'routes/components/dropdownWrapper'; import { DeleteModal } from '../deleteModal'; +import { TagMappingModal } from '../tagMappingModal'; interface ActionsKebabOwnProps { canWrite?: boolean; isDisabled?: boolean; item: SettingsData; - onDelete(); + onUpdate(); } type ActionsKebabProps = ActionsKebabOwnProps; -const ActionnsKebab: React.FC = ({ canWrite, isDisabled, item, onDelete }) => { - const [isOpen, setIsOpen] = useState(false); +const ActionnsKebab: React.FC = ({ canWrite, isDisabled, item, onUpdate }) => { + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [isTagMappingModalOpen, setIsTagMappingModalOpen] = useState(false); const intl = useIntl(); const getItems = () => { const items: DropdownWrapperItem[] = [ { isDisabled: isDisabled || !canWrite, - onClick: handleOnClick, + onClick: handleOnTagMappingModalClick, + toString: () => intl.formatMessage(messages.tagMappingAddChildTags), + ...(!canWrite && { + tooltipProps: { + content:
{intl.formatMessage(messages.readOnlyPermissions)}
, + }, + }), + }, + { + isDisabled: isDisabled || !canWrite, + onClick: handleOnDeleteModalClick, toString: () => intl.formatMessage(messages.tagMappingDelete), ...(!canWrite && { tooltipProps: { @@ -37,23 +49,37 @@ const ActionnsKebab: React.FC = ({ canWrite, isDisabled, item return items; }; - const handleOnClose = () => { - setIsOpen(false); + const handleOnDeleteModalClose = () => { + setIsDeleteModalOpen(false); + }; + + const handleOnDeleteModalClick = () => { + setIsDeleteModalOpen(!isDeleteModalOpen); }; - const handleOnClick = () => { - setIsOpen(!isOpen); + const handleOnTagMappingModalClose = () => { + setIsTagMappingModalOpen(false); + }; + + const handleOnTagMappingModalClick = () => { + setIsTagMappingModalOpen(!isTagMappingModalOpen); }; return ( <> + ); diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx index 014c43c14..c68191555 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx @@ -14,12 +14,12 @@ interface DeleteActionOwnProps { canWrite?: boolean; isDisabled?: boolean; item: SettingsData; - onDelete(); + onUpdate(); } type DeleteActionProps = DeleteActionOwnProps; -const DeleteAction: React.FC = ({ canWrite, isDisabled, item, onDelete }) => { +const DeleteAction: React.FC = ({ canWrite, isDisabled, item, onUpdate }) => { const [isOpen, setIsOpen] = useState(false); const intl = useIntl(); @@ -63,7 +63,7 @@ const DeleteAction: React.FC = ({ canWrite, isDisabled, item, isOpen={isOpen} item={item} onClose={handleOnClose} - onDelete={onDelete} + onUpdate={onUpdate} settingsType={SettingsType.tagsMappingsChildRemove} /> diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx index a084f3b6a..8717af6db 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx @@ -19,7 +19,7 @@ interface DeleteModalOwnProps { isOpen?: boolean; item: SettingsData; onClose(); - onDelete(); + onUpdate(); settingsType: SettingsType; } @@ -34,7 +34,7 @@ interface DeleteModalStateProps { type DeleteModalProps = DeleteModalOwnProps; -const DeleteModal: React.FC = ({ isOpen, item, onClose, onDelete, settingsType }) => { +const DeleteModal: React.FC = ({ isOpen, item, onClose, onUpdate, settingsType }) => { const [isFinish, setIsFinish] = useState(false); const { settingsUpdateError, settingsUpdateStatus } = useMapToProps({ settingsType }); @@ -54,7 +54,7 @@ const DeleteModal: React.FC = ({ isOpen, item, onClose, onDele useEffect(() => { if (isFinish && settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { - onDelete(); + onUpdate(); } }, [isFinish, settingsUpdateError, settingsUpdateStatus]); diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.styles.ts b/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.styles.ts new file mode 100644 index 000000000..5b5e7a441 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.styles.ts @@ -0,0 +1,8 @@ +import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; +import type React from 'react'; + +export const styles = { + alertContainer: { + paddingBottom: global_spacer_md.value, + }, +} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx b/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx index 0e0e24116..bede3fbdc 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx @@ -1,55 +1,62 @@ -import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; -import { Modal, ModalBody, ModalHeader, ModalVariant } from '@patternfly/react-core/next'; +import { Alert, Button } from '@patternfly/react-core'; +import { Modal, ModalBody, ModalFooter, ModalHeader, ModalVariant } from '@patternfly/react-core/next'; import type { SettingsData } from 'api/settings'; +import { SettingsType } from 'api/settings'; +import type { AxiosError } from 'axios'; import messages from 'locales/messages'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; -import { ChildTags } from 'routes/settings/tagLabels/tagMapping/components/childTags'; -import { useStateCallback } from 'utils/hooks'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import { parseApiError } from 'routes/settings/tagLabels/tagMapping/utils/parseApiError'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { settingsActions, settingsSelectors } from 'store/settings'; + +import { ChildTags } from '../childTags'; +import { styles } from './tagMappingModal.styles'; interface TagMappingModalOwnProps { - canWrite?: boolean; - isDisabled?: boolean; + isOpen?: boolean; + item: SettingsData; + onClose(); + onUpdate(); +} + +interface TagMappingModalStateProps { + settingsUpdateError?: AxiosError; + settingsUpdateStatus?: FetchStatus; } type TagMappingModalProps = TagMappingModalOwnProps; -const TagMappingModal: React.FC = ({ canWrite, isDisabled }: TagMappingModalProps) => { - const [isOpen, setIsOpen] = useState(false); - const [selectedItems, setSelectedItems] = useStateCallback([]); +const TagMappingModal: React.FC = ({ isOpen, item: parent, onClose, onUpdate }) => { + const [childTags, setChildTags] = useState([]); + const [isFinish, setIsFinish] = useState(false); + const { settingsUpdateError, settingsUpdateStatus } = useMapToProps(); + const dispatch: ThunkDispatch = useDispatch(); const intl = useIntl(); - const getActions = () => { - const getTooltip = children => { - if (!canWrite) { - const disableTagsTooltip = intl.formatMessage(messages.readOnlyPermissions); - return {children}; - } - return children; - }; - - return getTooltip( - - ); - }; - const handleOnBulkSelect = (items: SettingsData[]) => { - setSelectedItems(items); - }; - - const handleOnClose = () => { - setIsOpen(false); + setChildTags(items); }; - const handleOnClick = () => { - setIsOpen(!isOpen); + const handleOnCreateTagMapping = () => { + if (settingsUpdateStatus !== FetchStatus.inProgress) { + setIsFinish(true); + dispatch( + settingsActions.updateSettings(SettingsType.tagsMappingsChildAdd, { + parent: parent.uuid, + children: childTags.map(child => child.uuid), + }) + ); + } }; const handleOnSelect = (items: SettingsData[], isSelected: boolean = false) => { - let newItems = [...selectedItems]; + let newItems = [...childTags]; if (items && items.length > 0) { if (isSelected) { items.map(item => newItems.push(item)); @@ -59,27 +66,59 @@ const TagMappingModal: React.FC = ({ canWrite, isDisabled }); } } - setSelectedItems(newItems); + setChildTags(newItems); }; - // const handleOnCreateTagMapping = () => { - // // TBD... - // }; + useEffect(() => { + if (isFinish && settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { + onUpdate(); + } + }, [isFinish, settingsUpdateError, settingsUpdateStatus]); // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree return ( - <> - {getActions()} - - - - - - - + + + + {settingsUpdateStatus === FetchStatus.complete && settingsUpdateError && ( +
+ +
+ )} +
{intl.formatMessage(messages.tagMappingAddChildTagsDesc, { value: {parent.key} })}
+ +
+ + + + +
+ ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = (): TagMappingModalStateProps => { + const settingsUpdateStatus = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildAdd) + ); + const settingsUpdateError = useSelector((state: RootState) => + settingsSelectors.selectSettingsUpdateError(state, SettingsType.tagsMappingsChildAdd) ); + + return { + settingsUpdateError, + settingsUpdateStatus, + }; }; export default TagMappingModal; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx index c204890d8..a612ec8bf 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx @@ -38,7 +38,7 @@ const TagMappingWizard: React.FC = ({ onClose, }: TagMappingWizardProps) => { const [childTags, setChildTags] = useState([]); - const [isFinished, setIsFinished] = useState(false); + const [isFinish, setIsFinish] = useState(false); const [isOpen, setIsOpen] = useState(false); const [parentTags, setParentTags] = useState([]); @@ -180,7 +180,7 @@ const TagMappingWizard: React.FC = ({ const handleOnReset = () => { setChildTags([]); setParentTags([]); - setIsFinished(false); + setIsFinish(false); }; const handleOnSelectChild = (items: SettingsData[], isSelected: boolean = false) => { @@ -213,7 +213,7 @@ const TagMappingWizard: React.FC = ({ useEffect(() => { if (isOpen && settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { - setIsFinished(true); + setIsFinish(true); } }, [settingsUpdateError, settingsUpdateStatus]); @@ -227,7 +227,7 @@ const TagMappingWizard: React.FC = ({ return ( <> {getActions()} - {isFinished ? getSuccessEmptyState() : getWizard()} + {isFinish ? getSuccessEmptyState() : getWizard()} ); }; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx index 7bfcb0452..e0aee10d6 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx @@ -17,6 +17,7 @@ import type { AxiosError } from 'axios'; import messages from 'locales/messages'; import React from 'react'; import { useIntl } from 'react-intl'; +import { parseApiError } from 'routes/settings/tagLabels/tagMapping/utils/parseApiError'; import { FetchStatus } from 'store/common'; import { styles } from './tagMappingWizard.styles'; @@ -38,20 +39,6 @@ const TagMappingWizardReview: React.FC = ({ }: TagMappingWizardReviewProps) => { const intl = useIntl(); - const parseApiError = error => { - if (error.response && error.response.data) { - if (error.response.data.Error) { - return error.response.data.Error; - } - if (error.response.data.errors) { - return error.response.data.errors.map(er => `${er.source}: ${er.detail}`).join(', '); - } - } else if (error.message) { - return error.message; - } - return 'unknown'; - }; - return ( <> {settingsStatus === FetchStatus.complete && settingsError && ( diff --git a/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx b/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx index cf5f3e0c7..1f50cbe06 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx @@ -97,8 +97,8 @@ const TagMapping: React.FC = ({ canWrite }) => { filterBy={query.filter_by} isDisabled={isDisabled} isLoading={settingsStatus === FetchStatus.inProgress} - onDelete={handleOnDelete} onSort={(sortType, isSortAscending) => handleOnSort(sortType, isSortAscending)} + onUpdate={handleOnUpdate} orderBy={query.order_by} settings={settings} /> @@ -127,10 +127,6 @@ const TagMapping: React.FC = ({ canWrite }) => { refresh(); }; - const handleOnDelete = () => { - refresh(); - }; - const handleOnFilterAdded = filter => { const newQuery = queryUtils.handleOnFilterAdded(query, filter); setQuery(newQuery); @@ -156,6 +152,10 @@ const TagMapping: React.FC = ({ canWrite }) => { setQuery(newQuery); }; + const handleOnUpdate = () => { + refresh(); + }; + // Force refresh const refresh = () => { setQuery({ ...query }); diff --git a/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx index a9e3303da..e07647c42 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx @@ -15,8 +15,8 @@ interface TagMappingTableOwnProps { filterBy?: any; isDisabled?: boolean; isLoading?: boolean; - onDelete(); onSort(value: string, isSortAscending: boolean); + onUpdate(); orderBy?: any; settings: Settings; } @@ -28,8 +28,8 @@ const TagMappingTable: React.FC = ({ filterBy, isDisabled, isLoading, - onDelete, onSort, + onUpdate, orderBy, settings, }) => { @@ -77,7 +77,7 @@ const TagMappingTable: React.FC = ({ value: intl.formatMessage(messages.sourceTypes, { value: parent?.source_type?.toLowerCase() }), }, { - value: , + value: , }, ], children: parent.children.map(child => { @@ -93,7 +93,7 @@ const TagMappingTable: React.FC = ({ style: styles.expandableRowContent, }, { - value: , + value: , }, ], item: child, diff --git a/src/routes/settings/tagLabels/tagMapping/utils/parseApiError.ts b/src/routes/settings/tagLabels/tagMapping/utils/parseApiError.ts new file mode 100644 index 000000000..272940840 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/utils/parseApiError.ts @@ -0,0 +1,13 @@ +export const parseApiError = error => { + if (error.response && error.response.data) { + if (error.response.data.Error) { + return error.response.data.Error; + } + if (error.response.data.errors) { + return error.response.data.errors.map(er => `${er.source}: ${er.detail}`).join(', '); + } + } else if (error.message) { + return error.message; + } + return 'unknown'; +}; From 957fa019f8a252d27f3d8cd4cd60646cf7a69627 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Sun, 10 Mar 2024 22:50:07 -0400 Subject: [PATCH 36/61] Added descriptions to wizard steps --- locales/data.json | 36 +++++++++++++++++++ locales/translations.json | 4 +++ src/locales/messages.ts | 24 +++++++++++++ .../tagMappingWizard.styles.ts | 6 ++++ .../tagMappingWizard/tagMappingWizard.tsx | 29 ++++++++++++++- .../tagMappingWizardReview.tsx | 2 +- 6 files changed, 99 insertions(+), 2 deletions(-) diff --git a/locales/data.json b/locales/data.json index 01ecc1e3e..0cc445bed 100644 --- a/locales/data.json +++ b/locales/data.json @@ -12166,6 +12166,42 @@ "value": "learnMore" } ], + "tagMappingSelectChildTags": [ + { + "type": 0, + "value": "Select child tags" + } + ], + "tagMappingSelectChildTagsDesc": [ + { + "type": 0, + "value": "Select the child tags that you want to map to a parent key. Tags that have been already mapped will not be available for selection. " + }, + { + "type": 1, + "value": "learnMore" + } + ], + "tagMappingSelectParentTags": [ + { + "type": 0, + "value": "Select parent tag" + } + ], + "tagMappingSelectParentTagsDesc": [ + { + "type": 0, + "value": "Select a parent tag key that will be mapped to the " + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": " child tags you selected in the previous step. This tag will be available for filtering in Cost Management." + } + ], "tagMappingWarning": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index be5123b5c..87168e571 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -550,6 +550,10 @@ "tagMappingDelete": "Delete tag mapping", "tagMappingDeleteDesc": "This action will remove the {value} tag mapping. Changes will be reflected within 24 hours.", "tagMappingDesc": "Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}", + "tagMappingSelectChildTags": "Select child tags", + "tagMappingSelectChildTagsDesc": "Select the child tags that you want to map to a parent key. Tags that have been already mapped will not be available for selection. {learnMore}", + "tagMappingSelectParentTags": "Select parent tag", + "tagMappingSelectParentTagsDesc": "Select a parent tag key that will be mapped to the {count} child tags you selected in the previous step. This tag will be available for filtering in Cost Management.", "tagMappingWarning": "You must enable tags to use tag mapping.", "tagMappingWizardDesc": "Map multiple tags across data sources to be used as a single tag key for report grouping and filtering. Changes will be reflected within 24 hours.", "tagMappingWizardNavToCreateTagMapping": "Create another tag mapping", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 4d3a2f6e7..1bfb43dab 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -3390,6 +3390,30 @@ export default defineMessages({ 'Combine multiple tags across your cloud integrations to group and filter similar tags with one tag key. {warning} Changes will be reflected within 24 hours. {learnMore}', id: 'tagMappingDesc', }, + tagMappingSelectChildTags: { + defaultMessage: 'Select child tags', + description: 'Select child tags', + id: 'tagMappingSelectChildTags', + }, + tagMappingSelectChildTagsDesc: { + defaultMessage: + 'Select the child tags that you want to map to a parent key. Tags that have been already mapped will not be available for selection. {learnMore}', + description: + 'Select the child tags that you want to map to a parent key. Tags that have been already mapped will not be available for selection. {learnMore}', + id: 'tagMappingSelectChildTagsDesc', + }, + tagMappingSelectParentTags: { + defaultMessage: 'Select parent tag', + description: 'Select parent tag', + id: 'tagMappingSelectParentTags', + }, + tagMappingSelectParentTagsDesc: { + defaultMessage: + 'Select a parent tag key that will be mapped to the {count} child tags you selected in the previous step. This tag will be available for filtering in Cost Management.', + description: + 'Select a parent tag key that will be mapped to the {count} child tags you selected in the previous step. This tag will be available for filtering in Cost Management.', + id: 'tagMappingSelectParentTagsDesc', + }, tagMappingWarning: { defaultMessage: 'You must enable tags to use tag mapping.', description: 'You must enable tags to use tag mapping.', diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.styles.ts b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.styles.ts index c969cc33e..81737fd94 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.styles.ts +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.styles.ts @@ -6,6 +6,9 @@ export const styles = { alert: { marginBottom: global_FontSize_md.value, }, + descContainer: { + marginTop: global_FontSize_md.value, + }, emptyState: { margin: global_FontSize_md.value, }, @@ -16,6 +19,9 @@ export const styles = { backgroundColor: global_BackgroundColor_light_100.value, minHeight: '520px', }, + reviewDescContainer: { + marginBottom: global_FontSize_md.value, + }, reviewTable: { marginTop: '-10px', }, diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx index a612ec8bf..0f83eda56 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx @@ -1,4 +1,13 @@ -import { Button, ButtonVariant, Tooltip, Wizard, WizardHeader, WizardStep } from '@patternfly/react-core'; +import { + Button, + ButtonVariant, + Title, + TitleSizes, + Tooltip, + Wizard, + WizardHeader, + WizardStep, +} from '@patternfly/react-core'; import { Modal, ModalVariant } from '@patternfly/react-core/next'; import type { SettingsData } from 'api/settings'; import { SettingsType } from 'api/settings'; @@ -104,6 +113,18 @@ const TagMappingWizard: React.FC = ({ id="step-1" name={intl.formatMessage(messages.tagMappingWizardSelectChildTags)} > + + {intl.formatMessage(messages.tagMappingSelectChildTags)} + +
+ {intl.formatMessage(messages.tagMappingSelectChildTagsDesc, { + learnMore: ( + + {intl.formatMessage(messages.learnMore)} + + ), + })} +
= ({ isDisabled={childTags.length === 0} name={intl.formatMessage(messages.tagMappingWizardSelectParentTag)} > + + {intl.formatMessage(messages.tagMappingSelectParentTags)} + +
+ {intl.formatMessage(messages.tagMappingSelectParentTagsDesc, { count: {childTags.length} })} +
= ({ - + {intl.formatMessage(messages.tagMappingWizardReviewDesc, { create: {intl.formatMessage(messages.create)}, From d00f41bb13816ef2e34b716d018557855f41ef17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 10:38:09 +0000 Subject: [PATCH 37/61] (chore): Bump the lint-dependencies group with 2 updates Bumps the lint-dependencies group with 2 updates: [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) and [eslint-plugin-markdown](https://github.com/eslint/eslint-plugin-markdown). Updates `eslint-plugin-jsdoc` from 48.2.0 to 48.2.1 - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v48.2.0...v48.2.1) Updates `eslint-plugin-markdown` from 3.0.1 to 4.0.1 - [Release notes](https://github.com/eslint/eslint-plugin-markdown/releases) - [Changelog](https://github.com/eslint/eslint-plugin-markdown/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint-plugin-markdown/compare/v3.0.1...v4.0.1) --- updated-dependencies: - dependency-name: eslint-plugin-jsdoc dependency-type: direct:development update-type: version-update:semver-patch dependency-group: lint-dependencies - dependency-name: eslint-plugin-markdown dependency-type: direct:development update-type: version-update:semver-major dependency-group: lint-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 20 ++++++++++---------- package.json | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9cf87dbab..e9a722772 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,8 +65,8 @@ "eslint": "^8.57.0", "eslint-plugin-formatjs": "^4.12.2", "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-jsdoc": "^48.2.0", - "eslint-plugin-markdown": "^3.0.1", + "eslint-plugin-jsdoc": "^48.2.1", + "eslint-plugin-markdown": "^4.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.0", @@ -9448,9 +9448,9 @@ "dev": true }, "node_modules/eslint-plugin-jsdoc": { - "version": "48.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.0.tgz", - "integrity": "sha512-O2B1XLBJnUCRkggFzUQ+PBYJDit8iAgXdlu8ucolqGrbmOWPvttZQZX8d1sC0MbqDMSLs8SHSQxaNPRY1RQREg==", + "version": "48.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.1.tgz", + "integrity": "sha512-iUvbcyDZSO/9xSuRv2HQBw++8VkV/pt3UWtX9cpPH0l7GKPq78QC/6+PmyQHHvNZaTjAce6QVciEbnc6J/zH5g==", "dev": true, "dependencies": { "@es-joy/jsdoccomment": "~0.42.0", @@ -9583,18 +9583,18 @@ } }, "node_modules/eslint-plugin-markdown": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-markdown/-/eslint-plugin-markdown-3.0.1.tgz", - "integrity": "sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-markdown/-/eslint-plugin-markdown-4.0.1.tgz", + "integrity": "sha512-5/MnGvYU0i8MbHH5cg8S+Vl3DL+bqRNYshk1xUO86DilNBaxtTkhH+5FD0/yO03AmlI6+lfNFdk2yOw72EPzpA==", "dev": true, "dependencies": { "mdast-util-from-markdown": "^0.8.5" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": ">=8" } }, "node_modules/eslint-plugin-n": { diff --git a/package.json b/package.json index c314fff2f..cc6628ead 100644 --- a/package.json +++ b/package.json @@ -105,8 +105,8 @@ "eslint": "^8.57.0", "eslint-plugin-formatjs": "^4.12.2", "eslint-plugin-jest-dom": "^5.1.0", - "eslint-plugin-jsdoc": "^48.2.0", - "eslint-plugin-markdown": "^3.0.1", + "eslint-plugin-jsdoc": "^48.2.1", + "eslint-plugin-markdown": "^4.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.0", From 8dfa4bcf1df33d8ae45e6eee47501efd73f439e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 10:40:40 +0000 Subject: [PATCH 38/61] (chore): Bump the ci-dependencies group with 4 updates Bumps the ci-dependencies group with 4 updates: [@patternfly/react-component-groups](https://github.com/patternfly/react-component-groups), [qs](https://github.com/ljharb/qs), [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) and [typescript](https://github.com/Microsoft/TypeScript). Updates `@patternfly/react-component-groups` from 5.0.0 to 5.1.0 - [Release notes](https://github.com/patternfly/react-component-groups/releases) - [Commits](https://github.com/patternfly/react-component-groups/compare/v5.0.0...v5.1.0) Updates `qs` from 6.11.2 to 6.12.0 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.11.2...v6.12.0) Updates `@types/react-dom` from 18.2.20 to 18.2.21 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `typescript` from 5.3.3 to 5.4.2 - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml) - [Commits](https://github.com/Microsoft/TypeScript/compare/v5.3.3...v5.4.2) --- updated-dependencies: - dependency-name: "@patternfly/react-component-groups" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ci-dependencies - dependency-name: qs dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ci-dependencies - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: ci-dependencies - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-minor dependency-group: ci-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 50 +++++++++++++++++++++++++---------------------- package.json | 8 ++++---- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9cf87dbab..6a875a3bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "dependencies": { "@patternfly/patternfly": "^5.2.0", "@patternfly/react-charts": "^7.2.2", - "@patternfly/react-component-groups": "^5.0.0", + "@patternfly/react-component-groups": "^5.1.0", "@patternfly/react-core": "^5.2.0", "@patternfly/react-icons": "^5.2.0", "@patternfly/react-table": "^5.2.0", @@ -28,7 +28,7 @@ "date-fns": "^3.3.1", "js-file-download": "^0.4.12", "lodash": "^4.17.21", - "qs": "^6.11.2", + "qs": "^6.12.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intl": "^6.6.2", @@ -55,7 +55,7 @@ "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", "@types/react": "^18.2.63", - "@types/react-dom": "^18.2.20", + "@types/react-dom": "^18.2.21", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.1.1", @@ -86,7 +86,7 @@ "rimraf": "^5.0.5", "ts-jest": "^29.1.2", "ts-patch": "^3.1.2", - "typescript": "^5.3.3", + "typescript": "^5.4.2", "webpack-bundle-analyzer": "^4.10.1" }, "engines": { @@ -2248,9 +2248,9 @@ } }, "node_modules/@patternfly/react-component-groups": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-5.0.0.tgz", - "integrity": "sha512-ON4h4SKOCgLRgZLd/FOj44wU19ytvFPjflxPSYU0KfCWlVgb6F62+l316/Va/tzDo/AwZypnUxOEksXDv+C2+A==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-5.1.0.tgz", + "integrity": "sha512-DCEKc7Iyuf/7prI2a6mWyM/qwgBBtEzxDnNSpkZyes+Q3os0F5lUxW5qZVrg3JcNp0/J1183vwLdp1VoJRmcJw==", "dependencies": { "@patternfly/react-core": "^5.1.1", "@patternfly/react-icons": "^5.1.1", @@ -4075,9 +4075,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.20", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.20.tgz", - "integrity": "sha512-HXN/biJY8nv20Cn9ZbCFq3liERd4CozVZmKbaiZ9KiKTrWqsP7eoGDO6OOGvJQwoVFuiXaiJ7nBBjiFFbRmQMQ==", + "version": "18.2.21", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.21.tgz", + "integrity": "sha512-gnvBA/21SA4xxqNXEwNiVcP0xSGHh/gi1VhWv9Bl46a0ItbTT5nFY+G9VSQpaG/8N/qdJpJ+vftQ4zflTtnjLw==", "dev": true, "dependencies": { "@types/react": "*" @@ -17857,11 +17857,11 @@ ] }, "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", + "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -19012,13 +19012,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -20517,9 +20521,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "devOptional": true, "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index c314fff2f..93529b200 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "dependencies": { "@patternfly/patternfly": "^5.2.0", "@patternfly/react-charts": "^7.2.2", - "@patternfly/react-component-groups": "^5.0.0", + "@patternfly/react-component-groups": "^5.1.0", "@patternfly/react-core": "^5.2.0", "@patternfly/react-icons": "^5.2.0", "@patternfly/react-table": "^5.2.0", @@ -68,7 +68,7 @@ "date-fns": "^3.3.1", "js-file-download": "^0.4.12", "lodash": "^4.17.21", - "qs": "^6.11.2", + "qs": "^6.12.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intl": "^6.6.2", @@ -95,7 +95,7 @@ "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", "@types/react": "^18.2.63", - "@types/react-dom": "^18.2.20", + "@types/react-dom": "^18.2.21", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.1.1", @@ -126,7 +126,7 @@ "rimraf": "^5.0.5", "ts-jest": "^29.1.2", "ts-patch": "^3.1.2", - "typescript": "^5.3.3", + "typescript": "^5.4.2", "webpack-bundle-analyzer": "^4.10.1" }, "overrides": { From 6cd2871c3a20ee50562ced3cbe994e23a50ee1ed Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 11 Mar 2024 10:59:36 -0400 Subject: [PATCH 39/61] Added indent per UX feedback --- .../tagLabels/tagMapping/tagMapping.styles.ts | 13 ++++++++++--- .../tagLabels/tagMapping/tagMappingTable.tsx | 5 +++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts b/src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts index ed8cbcab5..a662aae35 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts +++ b/src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts @@ -1,4 +1,5 @@ import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; +import global_spacer_lg from '@patternfly/react-tokens/dist/js/global_spacer_lg'; import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; import global_spacer_sm from '@patternfly/react-tokens/dist/js/global_spacer_sm'; import type React from 'react'; @@ -7,12 +8,18 @@ export const styles = { action: { marginLeft: global_spacer_md.var, }, + childActionColumn: { + paddingRight: 0, + }, + childSourceTypeColumn: { + paddingRight: global_spacer_sm.value, + }, + childTagKeyColumn: { + paddingLeft: global_spacer_lg.value, + }, emptyStateContainer: { paddingTop: global_spacer_md.value, }, - expandableRowContent: { - paddingLeft: global_spacer_sm.value, - }, pagination: { backgroundColor: global_BackgroundColor_light_100.value, paddingBottom: global_spacer_md.value, diff --git a/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx index e07647c42..2d1c147f3 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx @@ -86,14 +86,15 @@ const TagMappingTable: React.FC = ({ {}, // Empty cell for expand toggle { value: child.key ? child.key : '', - style: styles.expandableRowContent, + style: styles.childTagKeyColumn, }, { value: intl.formatMessage(messages.sourceTypes, { value: child?.source_type?.toLowerCase() }), - style: styles.expandableRowContent, + style: styles.childSourceTypeColumn, }, { value: , + style: styles.childActionColumn, }, ], item: child, From cd52b2432cf91e9858a15077553819e100338c76 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 11 Mar 2024 11:03:09 -0400 Subject: [PATCH 40/61] Allow unrecognized units to pass thru localization code https://issues.redhat.com/browse/COST-4753 --- locales/data.json | 67 ++++++++++++++++++- locales/translations.json | 4 +- src/locales/messages.ts | 10 ++- .../reportSummary/reportSummaryItem.test.tsx | 2 +- .../components/rateForm/rateForm.tsx | 4 +- src/utils/format.ts | 14 ++-- 6 files changed, 87 insertions(+), 14 deletions(-) diff --git a/locales/data.json b/locales/data.json index 895b8c82f..78020bdbf 100644 --- a/locales/data.json +++ b/locales/data.json @@ -12299,6 +12299,14 @@ } ] }, + "cluster_month": { + "value": [ + { + "type": 0, + "value": "cluster-month" + } + ] + }, "core_hours": { "value": [ { @@ -12359,6 +12367,18 @@ } ] }, + "gib": { + "value": [ + { + "type": 1, + "value": "value" + }, + { + "type": 0, + "value": " GiB" + } + ] + }, "gibibyte_month": { "value": [ { @@ -12412,6 +12432,22 @@ { "type": 1, "value": "value" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "units" + } + ] + }, + "pvc_month": { + "value": [ + { + "type": 0, + "value": "PVC-month" } ] }, @@ -12443,6 +12479,14 @@ } ] }, + "cluster_month": { + "value": [ + { + "type": 0, + "value": "cluster-month" + } + ] + }, "core": { "value": [ { @@ -12491,6 +12535,14 @@ } ] }, + "gib": { + "value": [ + { + "type": 0, + "value": "GiB" + } + ] + }, "gibibyte_month": { "value": [ { @@ -12524,7 +12576,20 @@ ] }, "other": { - "value": [] + "value": [ + { + "type": 1, + "value": "units" + } + ] + }, + "pvc_month": { + "value": [ + { + "type": 0, + "value": "PVC-month" + } + ] }, "vm_hours": { "value": [ diff --git a/locales/translations.json b/locales/translations.json index e1720dd5a..54442960b 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -567,8 +567,8 @@ "toolBarPriceListMeasurementPlaceHolder": "Filter by measurements", "toolBarPriceListMetricPlaceHolder": "Filter by metrics", "typeaheadAriaClear": "Clear button and input", - "unitTooltips": "{units, select, byte_ms {{value} Byte-ms} core_hours {{value} core-hours} gb {{value} GB} gb_hours {{value} GB-hours} gb_mo {{value} GB-month} gb_ms {{value} GB-ms} gibibyte_month {{value} GiB-month} hour {{value} hours} hrs {{value} hours} ms {{value} milliseconds} vm_hours {{value} VM-hours} other {{value}}}", - "units": "{units, select, byte_ms {Byte-ms} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} vm_hours {VM-hours} other {}}", + "unitTooltips": "{units, select, byte_ms {{value} Byte-ms} cluster_month {cluster-month} core_hours {{value} core-hours} gb {{value} GB} gb_hours {{value} GB-hours} gb_mo {{value} GB-month} gb_ms {{value} GB-ms} gib {{value} GiB} gibibyte_month {{value} GiB-month} hour {{value} hours} hrs {{value} hours} ms {{value} milliseconds} pvc_month {PVC-month} vm_hours {{value} VM-hours} other {{value} {units}}}", + "units": "{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gib {GiB} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} vm_hours {VM-hours} other {{units}}}", "unknown": "Unknown", "usage": "Usage", "usageCostDesc": "The portion of cost calculated by applying hourly and/or monthly price list rates to metrics.", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 911e4db68..8cf1b635e 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -3483,17 +3483,20 @@ export default defineMessages({ defaultMessage: '{units, select, ' + 'byte_ms {{value} Byte-ms} ' + + 'cluster_month {cluster-month} ' + 'core_hours {{value} core-hours} ' + 'gb {{value} GB} ' + 'gb_hours {{value} GB-hours} ' + 'gb_mo {{value} GB-month} ' + 'gb_ms {{value} GB-ms} ' + + 'gib {{value} GiB} ' + 'gibibyte_month {{value} GiB-month} ' + 'hour {{value} hours} ' + 'hrs {{value} hours} ' + 'ms {{value} milliseconds} ' + + 'pvc_month {PVC-month} ' + 'vm_hours {{value} VM-hours} ' + - 'other {{value}}}', + 'other {{value} {units}}}', description: 'return value and unit based on key: "units"', id: 'unitTooltips', }, @@ -3501,18 +3504,21 @@ export default defineMessages({ defaultMessage: '{units, select, ' + 'byte_ms {Byte-ms} ' + + 'cluster_month {cluster-month} ' + 'core {core} ' + 'core_hours {core-hours} ' + 'gb {GB} ' + 'gb_hours {GB-hours} ' + 'gb_mo {GB-month} ' + 'gb_ms {GB-ms} ' + + 'gib {GiB} ' + 'gibibyte_month {GiB-month} ' + 'hour {hours} ' + 'hrs {hours} ' + 'ms {milliseconds} ' + + 'pvc_month {PVC-month} ' + 'vm_hours {VM-hours} ' + - 'other {}}', + 'other {{units}}}', description: 'return the proper unit label based on key: "units"', id: 'units', }, diff --git a/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx b/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx index e635b6404..f1cdf1024 100644 --- a/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx +++ b/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx @@ -19,7 +19,7 @@ test('formats value', () => { expect(screen.getByText(/label/i)).not.toBeNull(); expect( screen.getByText( - '{value} {units} ({percent} %){"percent":"10","units":"{units, select, byte_ms {Byte-ms} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} vm_hours {VM-hours} other {}}{}","value":100}' + '{value} {units} ({percent} %){"percent":"10","units":"{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gib {GiB} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} vm_hours {VM-hours} other {{units}}}{\\"units\\":\\"units\\"}","value":100}' ) ).not.toBeNull(); expect(screen.getByRole('progressbar').getAttribute('aria-valuenow')).toBe('10'); diff --git a/src/routes/settings/costModels/components/rateForm/rateForm.tsx b/src/routes/settings/costModels/components/rateForm/rateForm.tsx index 25a9871ff..69ec14508 100644 --- a/src/routes/settings/costModels/components/rateForm/rateForm.tsx +++ b/src/routes/settings/costModels/components/rateForm/rateForm.tsx @@ -70,9 +70,7 @@ const RateFormBase: React.FC = ({ currencyUnits, intl = defaultIn }; const getMeasurementDescription = (o, u) => { // Match message descriptor or default to API string - // units only works with Node, Cluster, and PVC. it does not need to be translated - // if the metric is CPU, Memory, or Storage, units will be like `core_hours` or `gb_hours` and must be translated - const units = u.toLowerCase().replace('-', '_'); + const units = intl.formatMessage(messages.units, { units: unitsLookupKey(u) }) || u; const desc = intl.formatMessage(messages.measurementValuesDesc, { value: o.toLowerCase().replace('-', '_'), units: units ? units : u, diff --git a/src/utils/format.ts b/src/utils/format.ts index 8e3a762b9..03874c435 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -127,16 +127,19 @@ export const formatUnits: Formatter = (value, units, options) => { switch (lookup) { case 'byte_ms': + case 'cluser_month': case 'core': case 'core_hours': - case 'hour': - case 'hrs': case 'gb': case 'gb_hours': case 'gb_mo': case 'gb_ms': + case 'gib': case 'gibibyte_month': + case 'hour': + case 'hrs': case 'ms': + case 'pvc_month': case 'tag_mo': case 'vm_hours': return formatUsage(fValue, options); @@ -241,24 +244,25 @@ export const unitsLookupKey = (units): string => { switch (lookup) { case 'byte_ms': + case 'cluser_month': case 'core': case 'core_hours': case 'gb': case 'gb_hours': case 'gb_mo': case 'gb_ms': - case 'cluser_month': - case 'pvc_month': + case 'gib': case 'gibibyte_month': case 'hour': case 'hrs': case 'ms': + case 'pvc_month': case 'tag_mo': case 'vm_hours': return lookup; case 'gb_month': return 'gb_mo'; default: - return undefined; + return units; } }; From 10c475355f06180e736437e5d66841a4acdf96a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:53:44 +0000 Subject: [PATCH 41/61] (chore): Bump the ci-dependencies group with 1 update Bumps the ci-dependencies group with 1 update: [date-fns](https://github.com/date-fns/date-fns). Updates `date-fns` from 3.3.1 to 3.4.0 - [Release notes](https://github.com/date-fns/date-fns/releases) - [Changelog](https://github.com/date-fns/date-fns/blob/main/CHANGELOG.md) - [Commits](https://github.com/date-fns/date-fns/compare/v3.3.1...v3.4.0) --- updated-dependencies: - dependency-name: date-fns dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ci-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index fc8ae4061..f22765e19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.2.2", "axios": "^1.6.7", - "date-fns": "^3.3.1", + "date-fns": "^3.4.0", "js-file-download": "^0.4.12", "lodash": "^4.17.21", "qs": "^6.12.0", @@ -7998,9 +7998,9 @@ } }, "node_modules/date-fns": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz", - "integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.4.0.tgz", + "integrity": "sha512-Akz4R8J9MXBsOgF1QeWeCsbv6pntT5KCPjU0Q9prBxVmWJYPLhwAIsNg3b0QAdr0ttiozYLD3L/af7Ra0jqYXw==", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" diff --git a/package.json b/package.json index 50fe5e123..7dd5f60fd 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.2.2", "axios": "^1.6.7", - "date-fns": "^3.3.1", + "date-fns": "^3.4.0", "js-file-download": "^0.4.12", "lodash": "^4.17.21", "qs": "^6.12.0", From 247e252b053ba9bec61823eb59bd852d36a6333a Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 11 Mar 2024 11:59:36 -0400 Subject: [PATCH 42/61] Github actions doesn't seem to recognize '@patternfly' --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c72494c5c..e775a1f9b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -20,11 +20,11 @@ updates: - "*eslint*" patternfly-dependencies: patterns: - - "@patternfly*" + - "*patternfly*" test-dependencies: patterns: - "*jest*" - - "@testing-library*" + - "*testing-library*" ci-dependencies: patterns: - "*" # update remaining deps in single PR From d0d9215dd8b656ebb320bf2f3ff0d0ed86e91f10 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 11 Mar 2024 12:41:50 -0400 Subject: [PATCH 43/61] Refactor directory names for tag mapping --- .../actionnsKebab.tsx => actions/actions.tsx} | 17 +++++++------ .../tagMapping/components/actions/index.ts | 1 + .../components/actionsKebab/index.ts | 1 - .../childTagMapping.styles.ts} | 1 - .../childTagMapping.tsx} | 14 +++++------ .../components/childTagMapping/index.ts | 1 + .../components/deleteModal/index.ts | 2 -- .../deleteTagMapping.scss} | 0 .../deleteTagMapping.tsx} | 16 ++++++------- .../deleteTagMappingAction.tsx} | 14 +++++------ .../components/deleteTagMapping/index.ts | 2 ++ .../components/parentTagMapping/index.ts | 1 + .../parentTagMapping.styles.ts} | 0 .../parentTagMapping.tsx} | 24 +++++++++---------- .../parentTagMappingEmptyState.tsx} | 13 ++++++---- .../parentTagMappingReview.tsx} | 12 +++++----- .../components/tagMappingModal/index.ts | 1 - .../components/tagMappingWizard/index.ts | 1 - .../tagMapping/tagMappingEmptyState.tsx | 4 ++-- .../tagLabels/tagMapping/tagMappingTable.tsx | 15 ++++++++---- .../tagMapping/tagMappingToolbar.tsx | 4 ++-- 21 files changed, 76 insertions(+), 68 deletions(-) rename src/routes/settings/tagLabels/tagMapping/components/{actionsKebab/actionnsKebab.tsx => actions/actions.tsx} (84%) create mode 100644 src/routes/settings/tagLabels/tagMapping/components/actions/index.ts delete mode 100644 src/routes/settings/tagLabels/tagMapping/components/actionsKebab/index.ts rename src/routes/settings/tagLabels/tagMapping/components/{tagMappingModal/tagMappingModal.styles.ts => childTagMapping/childTagMapping.styles.ts} (99%) rename src/routes/settings/tagLabels/tagMapping/components/{tagMappingModal/tagMappingModal.tsx => childTagMapping/childTagMapping.tsx} (92%) create mode 100644 src/routes/settings/tagLabels/tagMapping/components/childTagMapping/index.ts delete mode 100644 src/routes/settings/tagLabels/tagMapping/components/deleteModal/index.ts rename src/routes/settings/tagLabels/tagMapping/components/{deleteModal/deleteModal.scss => deleteTagMapping/deleteTagMapping.scss} (100%) rename src/routes/settings/tagLabels/tagMapping/components/{deleteModal/deleteModal.tsx => deleteTagMapping/deleteTagMapping.tsx} (86%) rename src/routes/settings/tagLabels/tagMapping/components/{deleteModal/deleteAction.tsx => deleteTagMapping/deleteTagMappingAction.tsx} (81%) create mode 100644 src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/index.ts create mode 100644 src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/index.ts rename src/routes/settings/tagLabels/tagMapping/components/{tagMappingWizard/tagMappingWizard.styles.ts => parentTagMapping/parentTagMapping.styles.ts} (100%) rename src/routes/settings/tagLabels/tagMapping/components/{tagMappingWizard/tagMappingWizard.tsx => parentTagMapping/parentTagMapping.tsx} (92%) rename src/routes/settings/tagLabels/tagMapping/components/{tagMappingWizard/tagMappingWizardEmptyState.tsx => parentTagMapping/parentTagMappingEmptyState.tsx} (82%) rename src/routes/settings/tagLabels/tagMapping/components/{tagMappingWizard/tagMappingWizardReview.tsx => parentTagMapping/parentTagMappingReview.tsx} (92%) delete mode 100644 src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/index.ts delete mode 100644 src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/index.ts diff --git a/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx b/src/routes/settings/tagLabels/tagMapping/components/actions/actions.tsx similarity index 84% rename from src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx rename to src/routes/settings/tagLabels/tagMapping/components/actions/actions.tsx index a866ec8e5..dc9b4bbe2 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/actionnsKebab.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/actions/actions.tsx @@ -5,20 +5,19 @@ import React, { useState } from 'react'; import { useIntl } from 'react-intl'; import type { DropdownWrapperItem } from 'routes/components/dropdownWrapper'; import { DropdownWrapper } from 'routes/components/dropdownWrapper'; +import { ChildTagMapping } from 'routes/settings/tagLabels/tagMapping/components/childTagMapping'; +import { DeleteTagMapping } from 'routes/settings/tagLabels/tagMapping/components/deleteTagMapping'; -import { DeleteModal } from '../deleteModal'; -import { TagMappingModal } from '../tagMappingModal'; - -interface ActionsKebabOwnProps { +interface ActionsOwnProps { canWrite?: boolean; isDisabled?: boolean; item: SettingsData; onUpdate(); } -type ActionsKebabProps = ActionsKebabOwnProps; +type ActionsProps = ActionsOwnProps; -const ActionnsKebab: React.FC = ({ canWrite, isDisabled, item, onUpdate }) => { +const Actions: React.FC = ({ canWrite, isDisabled, item, onUpdate }) => { const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [isTagMappingModalOpen, setIsTagMappingModalOpen] = useState(false); const intl = useIntl(); @@ -67,14 +66,14 @@ const ActionnsKebab: React.FC = ({ canWrite, isDisabled, item return ( <> - - = ({ canWrite, isDisabled, item ); }; -export default ActionnsKebab; +export default Actions; diff --git a/src/routes/settings/tagLabels/tagMapping/components/actions/index.ts b/src/routes/settings/tagLabels/tagMapping/components/actions/index.ts new file mode 100644 index 000000000..0074c5577 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/actions/index.ts @@ -0,0 +1 @@ +export { default as Actions } from './actions'; diff --git a/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/index.ts b/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/index.ts deleted file mode 100644 index 85e7288ea..000000000 --- a/src/routes/settings/tagLabels/tagMapping/components/actionsKebab/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as ActionsKebab } from './actionnsKebab'; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.styles.ts b/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.styles.ts similarity index 99% rename from src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.styles.ts rename to src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.styles.ts index 5b5e7a441..5e4601d38 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.styles.ts +++ b/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.styles.ts @@ -1,6 +1,5 @@ import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; import type React from 'react'; - export const styles = { alertContainer: { paddingBottom: global_spacer_md.value, diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx b/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.tsx similarity index 92% rename from src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx rename to src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.tsx index bede3fbdc..9d27a65fb 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/tagMappingModal.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.tsx @@ -15,23 +15,23 @@ import { FetchStatus } from 'store/common'; import { settingsActions, settingsSelectors } from 'store/settings'; import { ChildTags } from '../childTags'; -import { styles } from './tagMappingModal.styles'; +import { styles } from './childTagMapping.styles'; -interface TagMappingModalOwnProps { +interface ChildTagMappingOwnProps { isOpen?: boolean; item: SettingsData; onClose(); onUpdate(); } -interface TagMappingModalStateProps { +interface ChildTagMappingStateProps { settingsUpdateError?: AxiosError; settingsUpdateStatus?: FetchStatus; } -type TagMappingModalProps = TagMappingModalOwnProps; +type ChildTagMappingProps = ChildTagMappingOwnProps; -const TagMappingModal: React.FC = ({ isOpen, item: parent, onClose, onUpdate }) => { +const ChildTagMapping: React.FC = ({ isOpen, item: parent, onClose, onUpdate }) => { const [childTags, setChildTags] = useState([]); const [isFinish, setIsFinish] = useState(false); const { settingsUpdateError, settingsUpdateStatus } = useMapToProps(); @@ -107,7 +107,7 @@ const TagMappingModal: React.FC = ({ isOpen, item: parent, }; // eslint-disable-next-line no-empty-pattern -const useMapToProps = (): TagMappingModalStateProps => { +const useMapToProps = (): ChildTagMappingStateProps => { const settingsUpdateStatus = useSelector((state: RootState) => settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildAdd) ); @@ -121,4 +121,4 @@ const useMapToProps = (): TagMappingModalStateProps => { }; }; -export default TagMappingModal; +export default ChildTagMapping; diff --git a/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/index.ts b/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/index.ts new file mode 100644 index 000000000..b7b26a638 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/index.ts @@ -0,0 +1 @@ +export { default as ChildTagMapping } from './childTagMapping'; diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/index.ts b/src/routes/settings/tagLabels/tagMapping/components/deleteModal/index.ts deleted file mode 100644 index 9a89892e6..000000000 --- a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as DeleteAction } from './deleteAction'; -export { default as DeleteModal } from './deleteModal'; diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.scss b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMapping.scss similarity index 100% rename from src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.scss rename to src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMapping.scss diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMapping.tsx similarity index 86% rename from src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx rename to src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMapping.tsx index 8717af6db..22ae1aa0a 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteModal.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMapping.tsx @@ -1,4 +1,4 @@ -import './deleteModal.scss'; +import './deleteTagMapping.scss'; import { Button } from '@patternfly/react-core'; import { Modal, ModalBody, ModalFooter, ModalHeader, ModalVariant } from '@patternfly/react-core/next'; @@ -15,7 +15,7 @@ import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; import { settingsActions, settingsSelectors } from 'store/settings'; -interface DeleteModalOwnProps { +interface DeleteTagMappingOwnProps { isOpen?: boolean; item: SettingsData; onClose(); @@ -23,18 +23,18 @@ interface DeleteModalOwnProps { settingsType: SettingsType; } -interface DeleteModalMapProps { +interface DeleteTagMappingMapProps { settingsType: SettingsType; } -interface DeleteModalStateProps { +interface DeleteTagMappingStateProps { settingsUpdateError?: AxiosError; settingsUpdateStatus?: FetchStatus; } -type DeleteModalProps = DeleteModalOwnProps; +type DeleteTagMappingProps = DeleteTagMappingOwnProps; -const DeleteModal: React.FC = ({ isOpen, item, onClose, onUpdate, settingsType }) => { +const DeleteTagMapping: React.FC = ({ isOpen, item, onClose, onUpdate, settingsType }) => { const [isFinish, setIsFinish] = useState(false); const { settingsUpdateError, settingsUpdateStatus } = useMapToProps({ settingsType }); @@ -82,7 +82,7 @@ const DeleteModal: React.FC = ({ isOpen, item, onClose, onUpda }; // eslint-disable-next-line no-empty-pattern -const useMapToProps = ({ settingsType }: DeleteModalMapProps): DeleteModalStateProps => { +const useMapToProps = ({ settingsType }: DeleteTagMappingMapProps): DeleteTagMappingStateProps => { const settingsUpdateStatus = useSelector((state: RootState) => settingsSelectors.selectSettingsUpdateStatus(state, settingsType) ); @@ -96,4 +96,4 @@ const useMapToProps = ({ settingsType }: DeleteModalMapProps): DeleteModalStateP }; }; -export default DeleteModal; +export default DeleteTagMapping; diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMappingAction.tsx similarity index 81% rename from src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx rename to src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMappingAction.tsx index c68191555..6778c8631 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/deleteModal/deleteAction.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMappingAction.tsx @@ -1,4 +1,4 @@ -import './deleteModal.scss'; +import './deleteTagMapping.scss'; import { Button, ButtonVariant, Tooltip } from '@patternfly/react-core'; import { MinusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/minus-circle-icon'; @@ -8,18 +8,18 @@ import messages from 'locales/messages'; import React, { useState } from 'react'; import { useIntl } from 'react-intl'; -import DeleteModal from './deleteModal'; +import DeleteTagMapping from './deleteTagMapping'; -interface DeleteActionOwnProps { +interface DeleteTagMappingActionOwnProps { canWrite?: boolean; isDisabled?: boolean; item: SettingsData; onUpdate(); } -type DeleteActionProps = DeleteActionOwnProps; +type DeleteTagMappingActionProps = DeleteTagMappingActionOwnProps; -const DeleteAction: React.FC = ({ canWrite, isDisabled, item, onUpdate }) => { +const DeleteTagMappingAction: React.FC = ({ canWrite, isDisabled, item, onUpdate }) => { const [isOpen, setIsOpen] = useState(false); const intl = useIntl(); @@ -59,7 +59,7 @@ const DeleteAction: React.FC = ({ canWrite, isDisabled, item, return ( <> {getActions()} - = ({ canWrite, isDisabled, item, ); }; -export default DeleteAction; +export default DeleteTagMappingAction; diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/index.ts b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/index.ts new file mode 100644 index 000000000..42a5f77fa --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/index.ts @@ -0,0 +1,2 @@ +export { default as DeleteTagMappingAction } from './deleteTagMappingAction'; +export { default as DeleteTagMapping } from './deleteTagMapping'; diff --git a/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/index.ts b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/index.ts new file mode 100644 index 000000000..ceafe1725 --- /dev/null +++ b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/index.ts @@ -0,0 +1 @@ +export { default as ParentTagMapping } from './parentTagMapping'; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.styles.ts b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMapping.styles.ts similarity index 100% rename from src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.styles.ts rename to src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMapping.styles.ts diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMapping.tsx similarity index 92% rename from src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx rename to src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMapping.tsx index 0f83eda56..503d8724b 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizard.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMapping.tsx @@ -24,28 +24,28 @@ import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; import { settingsActions, settingsSelectors } from 'store/settings'; -import { styles } from './tagMappingWizard.styles'; -import { TagMappingEmptyState } from './tagMappingWizardEmptyState'; -import { TagMappingWizardReview } from './tagMappingWizardReview'; +import { styles } from './parentTagMapping.styles'; +import { ParentTagMappingEmptyState } from './parentTagMappingEmptyState'; +import { ParentTagMappingReview } from './parentTagMappingReview'; -interface TagMappingWizardOwnProps { +interface ParentTagMappingOwnProps { canWrite?: boolean; isDisabled?: boolean; onClose(); } -interface TagMappingWizardStateProps { +interface ParentTagMappingStateProps { settingsUpdateError?: AxiosError; settingsUpdateStatus?: FetchStatus; } -type TagMappingWizardProps = TagMappingWizardOwnProps; +type ParentTagMappingProps = ParentTagMappingOwnProps; -const TagMappingWizard: React.FC = ({ +const ParentTagMapping: React.FC = ({ canWrite, isDisabled, onClose, -}: TagMappingWizardProps) => { +}: ParentTagMappingProps) => { const [childTags, setChildTags] = useState([]); const [isFinish, setIsFinish] = useState(false); const [isOpen, setIsOpen] = useState(false); @@ -82,7 +82,7 @@ const TagMappingWizard: React.FC = ({ title={intl.formatMessage(messages.createTagMapping)} />
- +
@@ -161,7 +161,7 @@ const TagMappingWizard: React.FC = ({ isDisabled={childTags.length === 0} name={intl.formatMessage(messages.tagMappingWizardReview)} > - = ({ ); }; -const useMapToProps = (): TagMappingWizardStateProps => { +const useMapToProps = (): ParentTagMappingStateProps => { const settingsUpdateStatus = useSelector((state: RootState) => settingsSelectors.selectSettingsUpdateStatus(state, SettingsType.tagsMappingsChildAdd) ); @@ -273,4 +273,4 @@ const useMapToProps = (): TagMappingWizardStateProps => { }; }; -export default TagMappingWizard; +export default ParentTagMapping; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardEmptyState.tsx b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingEmptyState.tsx similarity index 82% rename from src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardEmptyState.tsx rename to src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingEmptyState.tsx index 1f004491c..c10455276 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardEmptyState.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingEmptyState.tsx @@ -14,16 +14,19 @@ import messages from 'locales/messages'; import React from 'react'; import { useIntl } from 'react-intl'; -import { styles } from './tagMappingWizard.styles'; +import { styles } from './parentTagMapping.styles'; -interface TagMappingEmptyStateOwnProps { +interface ParentTagMappingEmptyStateOwnProps { onClose(event); onReset(event); } -type TagMappingEmptyStateProps = TagMappingEmptyStateOwnProps; +type ParentTagMappingEmptyStateProps = ParentTagMappingEmptyStateOwnProps; -const TagMappingEmptyState: React.FC = ({ onClose, onReset }: TagMappingEmptyStateProps) => { +const ParentTagMappingEmptyState: React.FC = ({ + onClose, + onReset, +}: ParentTagMappingEmptyStateProps) => { const intl = useIntl(); return ( @@ -63,4 +66,4 @@ const TagMappingEmptyState: React.FC = ({ onClose, on ); }; -export { TagMappingEmptyState }; +export { ParentTagMappingEmptyState }; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingReview.tsx similarity index 92% rename from src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx rename to src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingReview.tsx index fd03c37d3..bbad12703 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/tagMappingWizardReview.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingReview.tsx @@ -20,23 +20,23 @@ import { useIntl } from 'react-intl'; import { parseApiError } from 'routes/settings/tagLabels/tagMapping/utils/parseApiError'; import { FetchStatus } from 'store/common'; -import { styles } from './tagMappingWizard.styles'; +import { styles } from './parentTagMapping.styles'; -interface TagMappingWizardReviewOwnProps { +interface ParentTagMappingReviewOwnProps { childTags?: SettingsData[]; parentTags?: SettingsData[]; settingsError?: AxiosError; settingsStatus?: FetchStatus; } -type TagMappingWizardReviewProps = TagMappingWizardReviewOwnProps; +type ParentTagMappingReviewProps = ParentTagMappingReviewOwnProps; -const TagMappingWizardReview: React.FC = ({ +const ParentTagMappingReview: React.FC = ({ childTags = [], parentTags = [], settingsError, settingsStatus, -}: TagMappingWizardReviewProps) => { +}: ParentTagMappingReviewProps) => { const intl = useIntl(); return ( @@ -110,4 +110,4 @@ const TagMappingWizardReview: React.FC = ({ ); }; -export { TagMappingWizardReview }; +export { ParentTagMappingReview }; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/index.ts b/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/index.ts deleted file mode 100644 index 142245871..000000000 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as TagMappingModal } from './tagMappingModal'; diff --git a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/index.ts b/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/index.ts deleted file mode 100644 index e8b6e2c9d..000000000 --- a/src/routes/settings/tagLabels/tagMapping/components/tagMappingWizard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as TagMappingWizard } from './tagMappingWizard'; diff --git a/src/routes/settings/tagLabels/tagMapping/tagMappingEmptyState.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingEmptyState.tsx index c1e427f82..c5964cb81 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMappingEmptyState.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingEmptyState.tsx @@ -11,7 +11,7 @@ import { PlusCircleIcon } from '@patternfly/react-icons/dist/esm/icons/plus-circ import messages from 'locales/messages'; import React from 'react'; import { useIntl } from 'react-intl'; -import { TagMappingWizard } from 'routes/settings/tagLabels/tagMapping/components/tagMappingWizard'; +import { ParentTagMapping } from 'routes/settings/tagLabels/tagMapping/components/parentTagMapping'; interface TagMappingEmptyStateOwnProps { canWrite?: boolean; @@ -47,7 +47,7 @@ const TagMappingEmptyState: React.FC = ({ - + diff --git a/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx index 2d1c147f3..7135b3770 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx @@ -5,8 +5,8 @@ import messages from 'locales/messages'; import React, { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; import { ExpandableTable } from 'routes/components/dataTable'; -import { ActionsKebab } from 'routes/settings/tagLabels/tagMapping/components/actionsKebab'; -import { DeleteAction } from 'routes/settings/tagLabels/tagMapping/components/deleteModal'; +import { Actions } from 'routes/settings/tagLabels/tagMapping/components/actions'; +import { DeleteTagMappingAction } from 'routes/settings/tagLabels/tagMapping/components/deleteTagMapping'; import { styles } from './tagMapping.styles'; @@ -77,7 +77,7 @@ const TagMappingTable: React.FC = ({ value: intl.formatMessage(messages.sourceTypes, { value: parent?.source_type?.toLowerCase() }), }, { - value: , + value: , }, ], children: parent.children.map(child => { @@ -93,7 +93,14 @@ const TagMappingTable: React.FC = ({ style: styles.childSourceTypeColumn, }, { - value: , + value: ( + + ), style: styles.childActionColumn, }, ], diff --git a/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx index 9f6633782..dfb3da0b5 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { useIntl } from 'react-intl'; import { BasicToolbar } from 'routes/components/dataToolbar'; import type { ToolbarChipGroupExt } from 'routes/components/dataToolbar/utils/common'; -import { TagMappingWizard } from 'routes/settings/tagLabels/tagMapping/components/tagMappingWizard'; +import { ParentTagMapping } from 'routes/settings/tagLabels/tagMapping/components/parentTagMapping'; import type { Filter } from 'routes/utils/filter'; interface TagMappingToolbarOwnProps { @@ -87,7 +87,7 @@ const TagMappingToolbar: React.FC = ({ return ( } + actions={} categoryOptions={getCategoryOptions()} isAllSelected={isAllSelected} isDisabled={isDisabled} From 462a025a7e50e4bf36a71eaef9c508adeb0f7194 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 11 Mar 2024 13:03:19 -0400 Subject: [PATCH 44/61] Replace onUpdate with onClose https://issues.redhat.com/browse/COST-3824 --- .../tagMapping/components/actions/actions.tsx | 18 +++++++++--------- .../childTagMapping/childTagMapping.tsx | 5 ++--- .../deleteTagMapping/deleteTagMapping.tsx | 7 +++---- .../deleteTagMappingAction.tsx | 8 +++++--- .../parentTagMapping/parentTagMapping.tsx | 2 +- .../tagLabels/tagMapping/tagMapping.styles.ts | 3 ++- .../tagLabels/tagMapping/tagMapping.tsx | 8 ++------ .../tagLabels/tagMapping/tagMappingTable.tsx | 13 ++++--------- .../tagLabels/tagMapping/tagMappingToolbar.tsx | 6 +++--- 9 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src/routes/settings/tagLabels/tagMapping/components/actions/actions.tsx b/src/routes/settings/tagLabels/tagMapping/components/actions/actions.tsx index dc9b4bbe2..56c33598c 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/actions/actions.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/actions/actions.tsx @@ -12,12 +12,12 @@ interface ActionsOwnProps { canWrite?: boolean; isDisabled?: boolean; item: SettingsData; - onUpdate(); + onClose?: () => void; } type ActionsProps = ActionsOwnProps; -const Actions: React.FC = ({ canWrite, isDisabled, item, onUpdate }) => { +const Actions: React.FC = ({ canWrite, isDisabled, item, onClose }) => { const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [isTagMappingModalOpen, setIsTagMappingModalOpen] = useState(false); const intl = useIntl(); @@ -50,6 +50,9 @@ const Actions: React.FC = ({ canWrite, isDisabled, item, onUpdate const handleOnDeleteModalClose = () => { setIsDeleteModalOpen(false); + if (onClose) { + onClose(); + } }; const handleOnDeleteModalClick = () => { @@ -58,6 +61,9 @@ const Actions: React.FC = ({ canWrite, isDisabled, item, onUpdate const handleOnTagMappingModalClose = () => { setIsTagMappingModalOpen(false); + if (onClose) { + onClose(); + } }; const handleOnTagMappingModalClick = () => { @@ -70,15 +76,9 @@ const Actions: React.FC = ({ canWrite, isDisabled, item, onUpdate isOpen={isDeleteModalOpen} item={item} onClose={handleOnDeleteModalClose} - onUpdate={onUpdate} settingsType={SettingsType.tagsMappingsParentRemove} /> - + ); diff --git a/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.tsx b/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.tsx index 9d27a65fb..a79c1363d 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/childTagMapping/childTagMapping.tsx @@ -21,7 +21,6 @@ interface ChildTagMappingOwnProps { isOpen?: boolean; item: SettingsData; onClose(); - onUpdate(); } interface ChildTagMappingStateProps { @@ -31,7 +30,7 @@ interface ChildTagMappingStateProps { type ChildTagMappingProps = ChildTagMappingOwnProps; -const ChildTagMapping: React.FC = ({ isOpen, item: parent, onClose, onUpdate }) => { +const ChildTagMapping: React.FC = ({ isOpen, item: parent, onClose }) => { const [childTags, setChildTags] = useState([]); const [isFinish, setIsFinish] = useState(false); const { settingsUpdateError, settingsUpdateStatus } = useMapToProps(); @@ -71,7 +70,7 @@ const ChildTagMapping: React.FC = ({ isOpen, item: parent, useEffect(() => { if (isFinish && settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { - onUpdate(); + onClose(); } }, [isFinish, settingsUpdateError, settingsUpdateStatus]); diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMapping.tsx b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMapping.tsx index 22ae1aa0a..d6585bbdf 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMapping.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMapping.tsx @@ -18,8 +18,7 @@ import { settingsActions, settingsSelectors } from 'store/settings'; interface DeleteTagMappingOwnProps { isOpen?: boolean; item: SettingsData; - onClose(); - onUpdate(); + onClose?: () => void; settingsType: SettingsType; } @@ -34,7 +33,7 @@ interface DeleteTagMappingStateProps { type DeleteTagMappingProps = DeleteTagMappingOwnProps; -const DeleteTagMapping: React.FC = ({ isOpen, item, onClose, onUpdate, settingsType }) => { +const DeleteTagMapping: React.FC = ({ isOpen, item, onClose, settingsType }) => { const [isFinish, setIsFinish] = useState(false); const { settingsUpdateError, settingsUpdateStatus } = useMapToProps({ settingsType }); @@ -54,7 +53,7 @@ const DeleteTagMapping: React.FC = ({ isOpen, item, onClo useEffect(() => { if (isFinish && settingsUpdateStatus === FetchStatus.complete && !settingsUpdateError) { - onUpdate(); + onClose(); } }, [isFinish, settingsUpdateError, settingsUpdateStatus]); diff --git a/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMappingAction.tsx b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMappingAction.tsx index 6778c8631..328c358bf 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMappingAction.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/deleteTagMapping/deleteTagMappingAction.tsx @@ -14,12 +14,12 @@ interface DeleteTagMappingActionOwnProps { canWrite?: boolean; isDisabled?: boolean; item: SettingsData; - onUpdate(); + onClose?: () => void; } type DeleteTagMappingActionProps = DeleteTagMappingActionOwnProps; -const DeleteTagMappingAction: React.FC = ({ canWrite, isDisabled, item, onUpdate }) => { +const DeleteTagMappingAction: React.FC = ({ canWrite, isDisabled, item, onClose }) => { const [isOpen, setIsOpen] = useState(false); const intl = useIntl(); @@ -47,6 +47,9 @@ const DeleteTagMappingAction: React.FC = ({ canWrit const handleOnClose = () => { setIsOpen(false); + if (onClose) { + onClose(); + } }; const handleOnClick = () => { @@ -63,7 +66,6 @@ const DeleteTagMappingAction: React.FC = ({ canWrit isOpen={isOpen} item={item} onClose={handleOnClose} - onUpdate={onUpdate} settingsType={SettingsType.tagsMappingsChildRemove} /> diff --git a/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMapping.tsx b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMapping.tsx index 503d8724b..5eca8b378 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMapping.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMapping.tsx @@ -31,7 +31,7 @@ import { ParentTagMappingReview } from './parentTagMappingReview'; interface ParentTagMappingOwnProps { canWrite?: boolean; isDisabled?: boolean; - onClose(); + onClose?: () => void; } interface ParentTagMappingStateProps { diff --git a/src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts b/src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts index a662aae35..fd7b8a0a8 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts +++ b/src/routes/settings/tagLabels/tagMapping/tagMapping.styles.ts @@ -1,4 +1,5 @@ import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; +import global_spacer_2xl from '@patternfly/react-tokens/dist/js/global_spacer_2xl'; import global_spacer_lg from '@patternfly/react-tokens/dist/js/global_spacer_lg'; import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; import global_spacer_sm from '@patternfly/react-tokens/dist/js/global_spacer_sm'; @@ -9,7 +10,7 @@ export const styles = { marginLeft: global_spacer_md.var, }, childActionColumn: { - paddingRight: 0, + paddingRight: global_spacer_2xl.value, }, childSourceTypeColumn: { paddingRight: global_spacer_sm.value, diff --git a/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx b/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx index 1f50cbe06..2f9a5258a 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMapping.tsx @@ -97,8 +97,8 @@ const TagMapping: React.FC = ({ canWrite }) => { filterBy={query.filter_by} isDisabled={isDisabled} isLoading={settingsStatus === FetchStatus.inProgress} + onClose={handleOnClose} onSort={(sortType, isSortAscending) => handleOnSort(sortType, isSortAscending)} - onUpdate={handleOnUpdate} orderBy={query.order_by} settings={settings} /> @@ -114,9 +114,9 @@ const TagMapping: React.FC = ({ canWrite }) => { isDisabled={mappings.length === 0} itemsPerPage={mappings.length} itemsTotal={itemsTotal} + onClose={handleOnClose} onFilterAdded={filter => handleOnFilterAdded(filter)} onFilterRemoved={filter => handleOnFilterRemoved(filter)} - onWizardClose={handleOnClose} pagination={getPagination(isDisabled)} query={query} /> @@ -152,10 +152,6 @@ const TagMapping: React.FC = ({ canWrite }) => { setQuery(newQuery); }; - const handleOnUpdate = () => { - refresh(); - }; - // Force refresh const refresh = () => { setQuery({ ...query }); diff --git a/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx index 7135b3770..a33795926 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx @@ -15,8 +15,8 @@ interface TagMappingTableOwnProps { filterBy?: any; isDisabled?: boolean; isLoading?: boolean; + onClose?: () => void; onSort(value: string, isSortAscending: boolean); - onUpdate(); orderBy?: any; settings: Settings; } @@ -28,8 +28,8 @@ const TagMappingTable: React.FC = ({ filterBy, isDisabled, isLoading, + onClose, onSort, - onUpdate, orderBy, settings, }) => { @@ -77,7 +77,7 @@ const TagMappingTable: React.FC = ({ value: intl.formatMessage(messages.sourceTypes, { value: parent?.source_type?.toLowerCase() }), }, { - value: , + value: , }, ], children: parent.children.map(child => { @@ -94,12 +94,7 @@ const TagMappingTable: React.FC = ({ }, { value: ( - + ), style: styles.childActionColumn, }, diff --git a/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx b/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx index dfb3da0b5..eebe474d9 100644 --- a/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx +++ b/src/routes/settings/tagLabels/tagMapping/tagMappingToolbar.tsx @@ -13,9 +13,9 @@ interface TagMappingToolbarOwnProps { isDisabled?: boolean; itemsPerPage?: number; itemsTotal?: number; + onClose?: () => void; onFilterAdded(filter: Filter); onFilterRemoved(filter: Filter); - onWizardClose(); pagination?: React.ReactNode; query?: Query; } @@ -28,9 +28,9 @@ const TagMappingToolbar: React.FC = ({ isDisabled, itemsPerPage, itemsTotal, + onClose, onFilterAdded, onFilterRemoved, - onWizardClose, pagination, query, }) => { @@ -87,7 +87,7 @@ const TagMappingToolbar: React.FC = ({ return ( } + actions={} categoryOptions={getCategoryOptions()} isAllSelected={isAllSelected} isDisabled={isDisabled} From cc5bf1d08d74aec2eeb28dff36a8c6e35844aff8 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 11 Mar 2024 14:26:42 -0400 Subject: [PATCH 45/61] Add parent integration to review step --- locales/data.json | 10 ++++++++-- locales/translations.json | 5 +++-- src/locales/messages.ts | 11 ++++++++--- .../parentTagMapping/parentTagMappingReview.tsx | 16 ++++++++++------ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/locales/data.json b/locales/data.json index bbb8490c6..78feff806 100644 --- a/locales/data.json +++ b/locales/data.json @@ -7595,7 +7595,7 @@ "value": [ { "type": 0, - "value": "Tag Key" + "value": "Tag key" } ] }, @@ -12081,7 +12081,7 @@ "tagKeyChild": [ { "type": 0, - "value": "Child tag key" + "value": "Child tag keys" } ], "tagKeyParent": [ @@ -12090,6 +12090,12 @@ "value": "Parent tag key" } ], + "tagKeyParentSource": [ + { + "type": 0, + "value": "Parent integration" + } + ], "tagLabels": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index d364d3b07..dcd8aec43 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -308,7 +308,7 @@ "filterByTagValueAriaLabel": "Tag values", "filterByTagValueButtonAriaLabel": "Filter button for tag value", "filterByValuePlaceholder": "Filter by value", - "filterByValues": "{value, select, account {Account} aws_category {Cost category} cluster {Cluster} container {Container} default {Default} gcp_project {GCP project} group {Group} name {Name} node {Node} org_unit_id {Organizational unit} payer_tenant_id {Account} persistent_volume_claim {Persistent volume claim} product_service {Service} project {Project} region {Region} resource_location {Region} service {Service} service_name {Service} source_type {Integration} status {Status} storage_class {StorageClass} subscription_guid {Account} tag {Tag} tag_key {Tag Key} tag_key_child {Child tag Key} tag_key_parent {Parent tag Key} workload {Workload name} workload_type {Workload type} other {}}", + "filterByValues": "{value, select, account {Account} aws_category {Cost category} cluster {Cluster} container {Container} default {Default} gcp_project {GCP project} group {Group} name {Name} node {Node} org_unit_id {Organizational unit} payer_tenant_id {Account} persistent_volume_claim {Persistent volume claim} product_service {Service} project {Project} region {Region} resource_location {Region} service {Service} service_name {Service} source_type {Integration} status {Status} storage_class {StorageClass} subscription_guid {Account} tag {Tag} tag_key {Tag key} tag_key_child {Child tag Key} tag_key_parent {Parent tag Key} workload {Workload name} workload_type {Workload type} other {}}", "filterByValuesAriaLabel": "Values", "forDate": "{value} for {dateRange}", "gcp": "Google Cloud Platform", @@ -540,8 +540,9 @@ "tagHeadingKey": "Key", "tagHeadingTitle": "Tags ({value})", "tagHeadingValue": "Value", - "tagKeyChild": "Child tag key", + "tagKeyChild": "Child tag keys", "tagKeyParent": "Parent tag key", + "tagKeyParentSource": "Parent integration", "tagLabels": "Tags and labels", "tagLabelsEnable": "Enable tags and labels", "tagLabelsMap": "Map tags and labels", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 6bc823fba..4304ded68 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -1968,7 +1968,7 @@ export default defineMessages({ 'storage_class {StorageClass} ' + 'subscription_guid {Account} ' + 'tag {Tag} ' + - 'tag_key {Tag Key} ' + + 'tag_key {Tag key} ' + 'tag_key_child {Child tag Key} ' + 'tag_key_parent {Parent tag Key} ' + 'workload {Workload name} ' + @@ -3337,8 +3337,8 @@ export default defineMessages({ id: 'tagHeadingValue', }, tagKeyChild: { - defaultMessage: 'Child tag key', - description: 'Child tag key', + defaultMessage: 'Child tag keys', + description: 'Child tag keys', id: 'tagKeyChild', }, tagKeyParent: { @@ -3346,6 +3346,11 @@ export default defineMessages({ description: 'Parent tag key', id: 'tagKeyParent', }, + tagKeyParentSource: { + defaultMessage: 'Parent integration', + description: 'Parent integration', + id: 'tagKeyParentSource', + }, tagLabelsEnable: { defaultMessage: 'Enable tags and labels', description: 'Enable tags and labels', diff --git a/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingReview.tsx b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingReview.tsx index bbad12703..737294164 100644 --- a/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingReview.tsx +++ b/src/routes/settings/tagLabels/tagMapping/components/parentTagMapping/parentTagMappingReview.tsx @@ -83,7 +83,7 @@ const ParentTagMappingReview: React.FC = ({ {childTags.map((item, index) => ( - + {item.key} {intl.formatMessage(messages.sourceTypes, { value: item?.source_type?.toLowerCase() })} @@ -97,11 +97,15 @@ const ParentTagMappingReview: React.FC = ({ {intl.formatMessage(messages.tagKeyParent)} - {parentTags.map((val, index) => ( - - {val.key} - - ))} + {parentTags.map(item => item.key)} + + {intl.formatMessage(messages.tagKeyParentSource)} + + + {parentTags.map(item => + intl.formatMessage(messages.sourceTypes, { value: item?.source_type?.toLowerCase() }) + )} + From c130348b2e1a3f20f2f19127cdcb2f5048c13211 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Tue, 12 Mar 2024 08:01:56 -0400 Subject: [PATCH 46/61] Revert unit changes for COST-4754 --- locales/data.json | 15 +-------------- locales/translations.json | 4 ++-- src/locales/messages.ts | 4 ++-- .../reportSummary/reportSummaryItem.test.tsx | 2 +- 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/locales/data.json b/locales/data.json index 78feff806..4c649887a 100644 --- a/locales/data.json +++ b/locales/data.json @@ -12514,14 +12514,6 @@ { "type": 1, "value": "value" - }, - { - "type": 0, - "value": " " - }, - { - "type": 1, - "value": "units" } ] }, @@ -12658,12 +12650,7 @@ ] }, "other": { - "value": [ - { - "type": 1, - "value": "units" - } - ] + "value": [] }, "pvc_month": { "value": [ diff --git a/locales/translations.json b/locales/translations.json index dcd8aec43..eed920457 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -576,8 +576,8 @@ "toolBarPriceListMeasurementPlaceHolder": "Filter by measurements", "toolBarPriceListMetricPlaceHolder": "Filter by metrics", "typeaheadAriaClear": "Clear button and input", - "unitTooltips": "{units, select, byte_ms {{value} Byte-ms} cluster_month {cluster-month} core_hours {{value} core-hours} gb {{value} GB} gb_hours {{value} GB-hours} gb_mo {{value} GB-month} gb_ms {{value} GB-ms} gib {{value} GiB} gibibyte_month {{value} GiB-month} hour {{value} hours} hrs {{value} hours} ms {{value} milliseconds} pvc_month {PVC-month} vm_hours {{value} VM-hours} other {{value} {units}}}", - "units": "{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gib {GiB} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} vm_hours {VM-hours} other {{units}}}", + "unitTooltips": "{units, select, byte_ms {{value} Byte-ms} cluster_month {cluster-month} core_hours {{value} core-hours} gb {{value} GB} gb_hours {{value} GB-hours} gb_mo {{value} GB-month} gb_ms {{value} GB-ms} gib {{value} GiB} gibibyte_month {{value} GiB-month} hour {{value} hours} hrs {{value} hours} ms {{value} milliseconds} pvc_month {PVC-month} vm_hours {{value} VM-hours} other {{value}}}", + "units": "{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gib {GiB} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} vm_hours {VM-hours} other {}}", "unknown": "Unknown", "usage": "Usage", "usageCostDesc": "The portion of cost calculated by applying hourly and/or monthly price list rates to metrics.", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 4304ded68..c68ab158e 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -3547,7 +3547,7 @@ export default defineMessages({ 'ms {{value} milliseconds} ' + 'pvc_month {PVC-month} ' + 'vm_hours {{value} VM-hours} ' + - 'other {{value} {units}}}', + 'other {{value}}}', description: 'return value and unit based on key: "units"', id: 'unitTooltips', }, @@ -3569,7 +3569,7 @@ export default defineMessages({ 'ms {milliseconds} ' + 'pvc_month {PVC-month} ' + 'vm_hours {VM-hours} ' + - 'other {{units}}}', + 'other {}}', description: 'return the proper unit label based on key: "units"', id: 'units', }, diff --git a/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx b/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx index f1cdf1024..99e6e4faa 100644 --- a/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx +++ b/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx @@ -19,7 +19,7 @@ test('formats value', () => { expect(screen.getByText(/label/i)).not.toBeNull(); expect( screen.getByText( - '{value} {units} ({percent} %){"percent":"10","units":"{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gib {GiB} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} vm_hours {VM-hours} other {{units}}}{\\"units\\":\\"units\\"}","value":100}' + '{value} {units} ({percent} %){"percent":"10","units":"{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gib {GiB} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} vm_hours {VM-hours} other {}}{\\"units\\":\\"units\\"}","value":100}' ) ).not.toBeNull(); expect(screen.getByRole('progressbar').getAttribute('aria-valuenow')).toBe('10'); From c3093b4cd743329eb470373d115316be38144008 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Tue, 12 Mar 2024 13:43:55 -0400 Subject: [PATCH 47/61] Add GiB-month and GiB-hours units --- locales/data.json | 52 ++++++++++++++++--- locales/translations.json | 4 +- src/locales/messages.ts | 12 +++-- .../reportSummary/reportSummaryItem.test.tsx | 2 +- src/utils/format.ts | 22 ++++---- 5 files changed, 70 insertions(+), 22 deletions(-) diff --git a/locales/data.json b/locales/data.json index 4c649887a..a4d006dc7 100644 --- a/locales/data.json +++ b/locales/data.json @@ -12425,7 +12425,7 @@ } ] }, - "gb_mo": { + "gb_month": { "value": [ { "type": 1, @@ -12449,7 +12449,7 @@ } ] }, - "gib": { + "gib_hours": { "value": [ { "type": 1, @@ -12457,7 +12457,19 @@ }, { "type": 0, - "value": " GiB" + "value": " GiB-hours" + } + ] + }, + "gib_month": { + "value": [ + { + "type": 1, + "value": "value" + }, + { + "type": 0, + "value": " GiB-month" } ] }, @@ -12525,6 +12537,18 @@ } ] }, + "tag_month": { + "value": [ + { + "type": 1, + "value": "value" + }, + { + "type": 0, + "value": " tag-month" + } + ] + }, "vm_hours": { "value": [ { @@ -12593,7 +12617,7 @@ } ] }, - "gb_mo": { + "gb_month": { "value": [ { "type": 0, @@ -12609,11 +12633,19 @@ } ] }, - "gib": { + "gib_hours": { "value": [ { "type": 0, - "value": "GiB" + "value": "GiB-hours" + } + ] + }, + "gib_month": { + "value": [ + { + "type": 0, + "value": "GiB-month" } ] }, @@ -12660,6 +12692,14 @@ } ] }, + "tag_month": { + "value": [ + { + "type": 0, + "value": "tag-month" + } + ] + }, "vm_hours": { "value": [ { diff --git a/locales/translations.json b/locales/translations.json index eed920457..8da0d2303 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -576,8 +576,8 @@ "toolBarPriceListMeasurementPlaceHolder": "Filter by measurements", "toolBarPriceListMetricPlaceHolder": "Filter by metrics", "typeaheadAriaClear": "Clear button and input", - "unitTooltips": "{units, select, byte_ms {{value} Byte-ms} cluster_month {cluster-month} core_hours {{value} core-hours} gb {{value} GB} gb_hours {{value} GB-hours} gb_mo {{value} GB-month} gb_ms {{value} GB-ms} gib {{value} GiB} gibibyte_month {{value} GiB-month} hour {{value} hours} hrs {{value} hours} ms {{value} milliseconds} pvc_month {PVC-month} vm_hours {{value} VM-hours} other {{value}}}", - "units": "{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gib {GiB} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} vm_hours {VM-hours} other {}}", + "unitTooltips": "{units, select, byte_ms {{value} Byte-ms} cluster_month {cluster-month} core_hours {{value} core-hours} gb {{value} GB} gb_hours {{value} GB-hours} gb_month {{value} GB-month} gb_ms {{value} GB-ms} gib_hours {{value} GiB-hours} gib_month {{value} GiB-month} gibibyte_month {{value} GiB-month} hour {{value} hours} hrs {{value} hours} ms {{value} milliseconds} pvc_month {PVC-month} tag_month {{value} tag-month} vm_hours {{value} VM-hours} other {{value}}}", + "units": "{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_month {GB-month} gb_ms {GB-ms} gib_hours {GiB-hours} gib_month {GiB-month} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} tag_month {tag-month} vm_hours {VM-hours} other {}}", "unknown": "Unknown", "usage": "Usage", "usageCostDesc": "The portion of cost calculated by applying hourly and/or monthly price list rates to metrics.", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index c68ab158e..75ffbf3c6 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -3538,14 +3538,16 @@ export default defineMessages({ 'core_hours {{value} core-hours} ' + 'gb {{value} GB} ' + 'gb_hours {{value} GB-hours} ' + - 'gb_mo {{value} GB-month} ' + + 'gb_month {{value} GB-month} ' + 'gb_ms {{value} GB-ms} ' + - 'gib {{value} GiB} ' + + 'gib_hours {{value} GiB-hours} ' + + 'gib_month {{value} GiB-month} ' + 'gibibyte_month {{value} GiB-month} ' + 'hour {{value} hours} ' + 'hrs {{value} hours} ' + 'ms {{value} milliseconds} ' + 'pvc_month {PVC-month} ' + + 'tag_month {{value} tag-month} ' + 'vm_hours {{value} VM-hours} ' + 'other {{value}}}', description: 'return value and unit based on key: "units"', @@ -3560,14 +3562,16 @@ export default defineMessages({ 'core_hours {core-hours} ' + 'gb {GB} ' + 'gb_hours {GB-hours} ' + - 'gb_mo {GB-month} ' + + 'gb_month {GB-month} ' + 'gb_ms {GB-ms} ' + - 'gib {GiB} ' + + 'gib_hours {GiB-hours} ' + + 'gib_month {GiB-month} ' + 'gibibyte_month {GiB-month} ' + 'hour {hours} ' + 'hrs {hours} ' + 'ms {milliseconds} ' + 'pvc_month {PVC-month} ' + + 'tag_month {tag-month} ' + 'vm_hours {VM-hours} ' + 'other {}}', description: 'return the proper unit label based on key: "units"', diff --git a/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx b/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx index 99e6e4faa..06f4cb778 100644 --- a/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx +++ b/src/routes/components/reports/reportSummary/reportSummaryItem.test.tsx @@ -19,7 +19,7 @@ test('formats value', () => { expect(screen.getByText(/label/i)).not.toBeNull(); expect( screen.getByText( - '{value} {units} ({percent} %){"percent":"10","units":"{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_mo {GB-month} gb_ms {GB-ms} gib {GiB} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} vm_hours {VM-hours} other {}}{\\"units\\":\\"units\\"}","value":100}' + '{value} {units} ({percent} %){"percent":"10","units":"{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_month {GB-month} gb_ms {GB-ms} gib_hours {GiB-hours} gib_month {GiB-month} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} tag_month {tag-month} vm_hours {VM-hours} other {}}{}","value":100}' ) ).not.toBeNull(); expect(screen.getByRole('progressbar').getAttribute('aria-valuenow')).toBe('10'); diff --git a/src/utils/format.ts b/src/utils/format.ts index 03874c435..4f77d2ed8 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -132,15 +132,16 @@ export const formatUnits: Formatter = (value, units, options) => { case 'core_hours': case 'gb': case 'gb_hours': - case 'gb_mo': + case 'gb_month': case 'gb_ms': - case 'gib': + case 'gib_hours': + case 'gib_month': case 'gibibyte_month': case 'hour': case 'hrs': case 'ms': case 'pvc_month': - case 'tag_mo': + case 'tag_month': case 'vm_hours': return formatUsage(fValue, options); } @@ -249,20 +250,23 @@ export const unitsLookupKey = (units): string => { case 'core_hours': case 'gb': case 'gb_hours': - case 'gb_mo': case 'gb_ms': - case 'gib': + case 'gib_hours': + case 'gib_month': case 'gibibyte_month': case 'hour': case 'hrs': case 'ms': case 'pvc_month': - case 'tag_mo': case 'vm_hours': return lookup; - case 'gb_month': - return 'gb_mo'; + case 'gib_mo': + return 'gib_month'; + case 'gb_mo': + return 'gb_month'; + case 'tag_mo': + return 'tag_month'; default: - return units; + return undefined; } }; From 9e9e3ca80ff591690ece961c5da8f5ef8742385e Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Tue, 12 Mar 2024 18:35:25 -0400 Subject: [PATCH 48/61] Added operator version to cluster info dialog https://issues.redhat.com/browse/COST-4559 --- locales/data.json | 12 +++++++++++ locales/translations.json | 2 ++ src/api/providers.ts | 8 +++++++ src/locales/messages.ts | 10 +++++++++ .../clusterInfo/cloudIntegration.tsx | 2 +- .../clusterInfo/clusterContent.tsx | 21 ++++++++++++++++--- .../clusterInfo/clusterInfo.styles.ts | 7 ++++++- 7 files changed, 57 insertions(+), 5 deletions(-) diff --git a/locales/data.json b/locales/data.json index a4d006dc7..c9df5fa12 100644 --- a/locales/data.json +++ b/locales/data.json @@ -1156,6 +1156,12 @@ "value": "Cost Management" } ], + "costManagementOperatorVersion": [ + { + "type": 0, + "value": "Cost Management operator version" + } + ], "costModels": [ { "type": 0, @@ -12719,6 +12725,12 @@ "value": "Unknown" } ], + "updateAvailable": [ + { + "type": 0, + "value": "Update available" + } + ], "usage": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 8da0d2303..23430bcdf 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -81,6 +81,7 @@ "costDistributionTitle": "Overhead cost breakdown", "costDistributionType": "{value, select, distributed {Distribute through cost models} total {Don't distribute overhead costs} other {}}", "costManagement": "Cost Management", + "costManagementOperatorVersion": "Cost Management operator version", "costModels": "Cost Models", "costModelsActions": "Cost model actions", "costModelsAddTagValues": "Add more tag values", @@ -579,6 +580,7 @@ "unitTooltips": "{units, select, byte_ms {{value} Byte-ms} cluster_month {cluster-month} core_hours {{value} core-hours} gb {{value} GB} gb_hours {{value} GB-hours} gb_month {{value} GB-month} gb_ms {{value} GB-ms} gib_hours {{value} GiB-hours} gib_month {{value} GiB-month} gibibyte_month {{value} GiB-month} hour {{value} hours} hrs {{value} hours} ms {{value} milliseconds} pvc_month {PVC-month} tag_month {{value} tag-month} vm_hours {{value} VM-hours} other {{value}}}", "units": "{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_month {GB-month} gb_ms {GB-ms} gib_hours {GiB-hours} gib_month {GiB-month} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} tag_month {tag-month} vm_hours {VM-hours} other {}}", "unknown": "Unknown", + "updateAvailable": "Update available", "usage": "Usage", "usageCostDesc": "The portion of cost calculated by applying hourly and/or monthly price list rates to metrics.", "usageCostTitle": "Usage cost", diff --git a/src/api/providers.ts b/src/api/providers.ts index 73a89025c..fa0d9dff7 100644 --- a/src/api/providers.ts +++ b/src/api/providers.ts @@ -2,6 +2,13 @@ import axios from 'axios'; import type { PagedMetaData, PagedResponse } from './api'; +export interface ProviderAdditionalContext { + operator_version?: string; + operator_airgapped?: boolean; + operator_certified?: boolean; + operator_update_available?: boolean; +} + export interface ProviderAuthentication { credentials?: { cluster_id?: string; @@ -40,6 +47,7 @@ export interface ProviderInfrastructure { export interface Provider { active?: boolean; + additional_context?: ProviderAdditionalContext; authentication?: ProviderAuthentication; billing_source?: ProviderBillingSource; created_by?: ProviderCreatedBy; diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 75ffbf3c6..5aa1d3000 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -461,6 +461,11 @@ export default defineMessages({ description: 'Cost Management', id: 'costManagement', }, + costManagementOperatorVersion: { + defaultMessage: 'Cost Management operator version', + description: 'Cost Management operator version', + id: 'costManagementOperatorVersion', + }, costModels: { defaultMessage: 'Cost Models', description: 'Cost Models', @@ -3582,6 +3587,11 @@ export default defineMessages({ description: 'Unknown', id: 'unknown', }, + updateAvailable: { + defaultMessage: 'Update available', + description: 'Update available', + id: 'updateAvailable', + }, usage: { defaultMessage: 'Usage', description: 'Usage', diff --git a/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx index b5ecf9313..f421f063e 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx @@ -61,7 +61,7 @@ const CloudIntegration: React.FC = ({ uuid }: CloudIntegr {intl.formatMessage(messages.cloudIntegration)} - + {intl.formatMessage(messages.source, { value: provider?.source_type?.toLowerCase() })} {provider?.name} diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx b/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx index 69585f69d..c30165032 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx @@ -1,6 +1,7 @@ import './clusterContent.scss'; -import { Text, TextContent, TextList, TextListItem, TextVariants } from '@patternfly/react-core'; +import { Icon, Text, TextContent, TextList, TextListItem, TextVariants } from '@patternfly/react-core'; +import { ExclamationTriangleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon'; import type { Providers } from 'api/providers'; import { ProviderType } from 'api/providers'; import { getProvidersQuery } from 'api/queries/providersQuery'; @@ -71,7 +72,7 @@ const ClusterContent: React.FC = ({ clusterId }: Cluste {intl.formatMessage(messages.clusterId)} - {clusterId} + {clusterId} {intl.formatMessage(messages.ocpClusterDetails)} {clusterInfo?.cost_models?.length === 0 && ( @@ -80,12 +81,26 @@ const ClusterContent: React.FC = ({ clusterId }: Cluste )} + {intl.formatMessage(messages.costManagementOperatorVersion)} + + + {clusterInfo?.additional_context?.operator_version} + {clusterInfo?.additional_context?.operator_update_available && ( + <> + + + + {intl.formatMessage(messages.updateAvailable)} + + )} + + {clusterInfo && ( <> {intl.formatMessage(messages.redHatIntegration)} - {intl.formatMessage(messages.source, { value: 'ocp' })} + {intl.formatMessage(messages.source, { value: 'ocp' })} {clusterInfo.name} diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts index 44efd2022..9ddc0c4e2 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts @@ -1,6 +1,7 @@ import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; import global_FontSize_md from '@patternfly/react-tokens/dist/js/global_FontSize_md'; import global_FontSize_xs from '@patternfly/react-tokens/dist/js/global_FontSize_xs'; +import global_warning_color_100 from '@patternfly/react-tokens/dist/js/global_warning_color_100'; import type React from 'react'; export const styles = { @@ -14,7 +15,11 @@ export const styles = { backgroundColor: global_BackgroundColor_light_100.value, minHeight: '520px', }, - spacing: { + spacingRight: { marginRight: global_FontSize_md.value, }, + updateAvailable: { + color: global_warning_color_100.value, + paddingLeft: '5px', + }, } as { [className: string]: React.CSSProperties }; From d80b91cab9d7b45e9c2031b9812eb03951810468 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Tue, 12 Mar 2024 21:05:26 -0400 Subject: [PATCH 49/61] Refactor cluster info component names --- .../ocpBreakdown/clusterInfo/cloudIntegration.tsx | 6 +----- .../details/ocpBreakdown/clusterInfo/clusterInfo.tsx | 6 +++--- .../{clusterContent.scss => clusterInfoContent.scss} | 0 .../{clusterContent.tsx => clusterInfoContent.tsx} | 12 ++++-------- src/routes/details/ocpBreakdown/clusterInfo/index.ts | 1 + src/routes/details/ocpBreakdown/ocpBreakdown.tsx | 2 +- 6 files changed, 10 insertions(+), 17 deletions(-) rename src/routes/details/ocpBreakdown/clusterInfo/{clusterContent.scss => clusterInfoContent.scss} (100%) rename src/routes/details/ocpBreakdown/clusterInfo/{clusterContent.tsx => clusterInfoContent.tsx} (94%) create mode 100644 src/routes/details/ocpBreakdown/clusterInfo/index.ts diff --git a/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx index f421f063e..c5675b499 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx @@ -22,7 +22,7 @@ interface CloudIntegrationOwnProps { uuid?: string; } -export interface CloudIntegrationStateProps { +interface CloudIntegrationStateProps { provider: Provider; providerError: AxiosError; providerFetchStatus: FetchStatus; @@ -30,10 +30,6 @@ export interface CloudIntegrationStateProps { uuid?: string; } -export interface CloudIntegrationMapProps { - // TBD... -} - type CloudIntegrationProps = CloudIntegrationOwnProps; const CloudIntegration: React.FC = ({ uuid }: CloudIntegrationProps) => { diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx index ad2631f34..fb74fcac8 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx @@ -4,8 +4,8 @@ import messages from 'locales/messages'; import React, { useState } from 'react'; import { useIntl } from 'react-intl'; -import { ClusterContent } from './clusterContent'; import { styles } from './clusterInfo.styles'; +import { ClusterInfoContent } from './clusterInfoContent'; interface ClusterInfoOwnProps { clusterId?: string; @@ -36,11 +36,11 @@ const ClusterInfo: React.FC = ({ clusterId }: ClusterInfoProps - + ); }; -export { ClusterInfo }; +export default ClusterInfo; diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.scss b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.scss similarity index 100% rename from src/routes/details/ocpBreakdown/clusterInfo/clusterContent.scss rename to src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.scss diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx similarity index 94% rename from src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx rename to src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx index c30165032..83796f3f5 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterContent.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx @@ -1,4 +1,4 @@ -import './clusterContent.scss'; +import './clusterInfoContent.scss'; import { Icon, Text, TextContent, TextList, TextListItem, TextVariants } from '@patternfly/react-core'; import { ExclamationTriangleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon'; @@ -28,20 +28,16 @@ interface ClusterInfoContentOwnProps { clusterId?: string; } -export interface ClusterInfoContentStateProps { +interface ClusterInfoContentStateProps { providers: Providers; providersError: AxiosError; providersFetchStatus: FetchStatus; providersQueryString: string; } -export interface ClusterInfoContentMapProps { - // TBD... -} - type ClusterInfoContentProps = ClusterInfoContentOwnProps; -const ClusterContent: React.FC = ({ clusterId }: ClusterInfoContentProps) => { +const ClusterInfoContent: React.FC = ({ clusterId }: ClusterInfoContentProps) => { const intl = useIntl(); const { providers, providersError, providersFetchStatus } = useMapToProps(); @@ -141,4 +137,4 @@ const useMapToProps = (): ClusterInfoContentStateProps => { }; }; -export { ClusterContent }; +export { ClusterInfoContent }; diff --git a/src/routes/details/ocpBreakdown/clusterInfo/index.ts b/src/routes/details/ocpBreakdown/clusterInfo/index.ts new file mode 100644 index 000000000..43774fc38 --- /dev/null +++ b/src/routes/details/ocpBreakdown/clusterInfo/index.ts @@ -0,0 +1 @@ +export { default as ClusterInfo } from './clusterInfo'; diff --git a/src/routes/details/ocpBreakdown/ocpBreakdown.tsx b/src/routes/details/ocpBreakdown/ocpBreakdown.tsx index d80e43760..6acf6aa0f 100644 --- a/src/routes/details/ocpBreakdown/ocpBreakdown.tsx +++ b/src/routes/details/ocpBreakdown/ocpBreakdown.tsx @@ -26,7 +26,7 @@ import type { RouterComponentProps } from 'utils/router'; import { withRouter } from 'utils/router'; import { getCostDistribution, getCurrency } from 'utils/sessionStorage'; -import { ClusterInfo } from './clusterInfo/clusterInfo'; +import { ClusterInfo } from './clusterInfo'; import { CostOverview } from './costOverview'; import { HistoricalData } from './historicalData'; import { OcpBreakdownOptimizations } from './ocpBreakdownOptimizations'; From 2623c0eac0fe6b15bcf98dd486210ce2d2f92cf0 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Tue, 12 Mar 2024 23:47:43 -0400 Subject: [PATCH 50/61] Create initial data details modal https://issues.redhat.com/browse/COST-4761 --- locales/data.json | 133 +++++++++++++ locales/translations.json | 7 + src/api/providers.ts | 5 + src/locales/messages.ts | 50 +++++ .../components/breakdown/breakdownBase.tsx | 3 + .../components/breakdown/breakdownHeader.tsx | 13 +- .../clusterInfo/clusterInfo.styles.ts | 7 +- .../dataDetails/cloudIntegration.tsx | 172 +++++++++++++++++ .../dataDetails/dataDetails.styles.ts | 30 +++ .../ocpBreakdown/dataDetails/dataDetails.tsx | 56 ++++++ .../dataDetails/dataDetailsContent.tsx | 176 ++++++++++++++++++ .../details/ocpBreakdown/dataDetails/index.ts | 1 + .../ocpBreakdown/dataDetails/utils/status.tsx | 25 +++ .../details/ocpBreakdown/ocpBreakdown.tsx | 2 + 14 files changed, 671 insertions(+), 9 deletions(-) create mode 100644 src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts create mode 100644 src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/index.ts create mode 100644 src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx diff --git a/locales/data.json b/locales/data.json index c9df5fa12..f2e523396 100644 --- a/locales/data.json +++ b/locales/data.json @@ -1093,6 +1093,12 @@ "value": "Cost categories" } ], + "costData": [ + { + "type": 0, + "value": "Cost data" + } + ], "costDistribution": [ { "type": 0, @@ -2368,6 +2374,133 @@ "value": "supplementaryCost" } ], + "dataDetails": [ + { + "type": 0, + "value": "Data details" + } + ], + "dataDetailsDownload": [ + { + "options": { + "complete": { + "value": [ + { + "type": 0, + "value": "Data download complete" + } + ] + }, + "in_progress": { + "value": [ + { + "type": 0, + "value": "Data download in progress" + } + ] + }, + "other": { + "value": [] + }, + "pending": { + "value": [ + { + "type": 0, + "value": "Data download pending" + } + ] + } + }, + "type": 5, + "value": "value" + } + ], + "dataDetailsProcessing": [ + { + "options": { + "complete": { + "value": [ + { + "type": 0, + "value": "Data processing complete" + } + ] + }, + "in_progress": { + "value": [ + { + "type": 0, + "value": "Data processing in progress" + } + ] + }, + "other": { + "value": [] + }, + "pending": { + "value": [ + { + "type": 0, + "value": "Data processing pending" + } + ] + } + }, + "type": 5, + "value": "value" + } + ], + "dataDetailsProgressStep": [ + { + "type": 0, + "value": "Data details progress step " + }, + { + "type": 1, + "value": "count" + } + ], + "dataDetailsProgressStepper": [ + { + "type": 0, + "value": "Data details progress stepper" + } + ], + "dataDetailsSummary": [ + { + "options": { + "complete": { + "value": [ + { + "type": 0, + "value": "Data summary complete" + } + ] + }, + "in_progress": { + "value": [ + { + "type": 0, + "value": "Data summary in progress" + } + ] + }, + "other": { + "value": [] + }, + "pending": { + "value": [ + { + "type": 0, + "value": "Data summary pending" + } + ] + } + }, + "type": 5, + "value": "value" + } + ], "dataTableAriaLabel": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 23430bcdf..4b850be93 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -74,6 +74,7 @@ "costCategoryDesc": "Enable your AWS cost categories to be used for report grouping and filtering. Changes will be reflected within 24 hours. {learnMore}", "costCategoryNames": "Cost category names", "costCategoryTitle": "Cost categories", + "costData": "Cost data", "costDistribution": "Cost distribution", "costDistributionAriaDesc": "Overhead cost breakdown of platform, worker unallocated, and total costs", "costDistributionAriaLabel": "A description of platform, worker unallocated, and total costs", @@ -197,6 +198,12 @@ "dashboardNetworkTitle": "Network services cost", "dashboardStorageTitle": "Storage services usage", "dashboardTotalCostTooltip": "This total cost is the sum of the infrastructure cost {infrastructureCost} and supplementary cost {supplementaryCost}", + "dataDetails": "Data details", + "dataDetailsDownload": "{value, select, complete {Data download complete}in_progress {Data download in progress}pending {Data download pending}other {}}", + "dataDetailsProcessing": "{value, select, complete {Data processing complete}in_progress {Data processing in progress}pending {Data processing pending}other {}}", + "dataDetailsProgressStep": "Data details progress step {count}", + "dataDetailsProgressStepper": "Data details progress stepper", + "dataDetailsSummary": "{value, select, complete {Data summary complete}in_progress {Data summary in progress}pending {Data summary pending}other {}}", "dataTableAriaLabel": "Details table", "datePickerAfterError": "Date is after the allowable range", "datePickerBeforeError": "Date is before the allowable range", diff --git a/src/api/providers.ts b/src/api/providers.ts index fa0d9dff7..2484b2127 100644 --- a/src/api/providers.ts +++ b/src/api/providers.ts @@ -62,6 +62,11 @@ export interface Provider { paused?: boolean; previous_month_data?: boolean; source_type?: string; + status?: { + download?: string; + processing?: string; + summary?: string; + }; type?: string; uuid?: string; } diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 5aa1d3000..0eb0609f5 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -422,6 +422,11 @@ export default defineMessages({ description: 'Cost categories', id: 'costCategoryTitle', }, + costData: { + defaultMessage: 'Cost data', + description: 'Cost data', + id: 'costData', + }, costDistribution: { defaultMessage: 'Cost distribution', description: 'Cost distribution', @@ -1117,6 +1122,51 @@ export default defineMessages({ description: 'total cost is the sum of the infrastructure cost and supplementary cost', id: 'dashboardTotalCostTooltip', }, + dataDetails: { + defaultMessage: 'Data details', + description: 'Data details', + id: 'dataDetails', + }, + dataDetailsDownload: { + defaultMessage: + '{value, select, ' + + 'complete {Data download complete}' + + 'in_progress {Data download in progress}' + + 'pending {Data download pending}' + + 'other {}}', + description: 'Data download status', + id: 'dataDetailsDownload', + }, + dataDetailsProcessing: { + defaultMessage: + '{value, select, ' + + 'complete {Data processing complete}' + + 'in_progress {Data processing in progress}' + + 'pending {Data processing pending}' + + 'other {}}', + description: 'Data processing status', + id: 'dataDetailsProcessing', + }, + dataDetailsProgressStep: { + defaultMessage: 'Data details progress step {count}', + description: 'Data details progress step {count}', + id: 'dataDetailsProgressStep', + }, + dataDetailsProgressStepper: { + defaultMessage: 'Data details progress stepper', + description: 'Data details progress stepper', + id: 'dataDetailsProgressStepper', + }, + dataDetailsSummary: { + defaultMessage: + '{value, select, ' + + 'complete {Data summary complete}' + + 'in_progress {Data summary in progress}' + + 'pending {Data summary pending}' + + 'other {}}', + description: 'Data summary status', + id: 'dataDetailsSummary', + }, dataTableAriaLabel: { defaultMessage: 'Details table', description: 'Details table', diff --git a/src/routes/details/components/breakdown/breakdownBase.tsx b/src/routes/details/components/breakdown/breakdownBase.tsx index 617e26431..e233c85fe 100644 --- a/src/routes/details/components/breakdown/breakdownBase.tsx +++ b/src/routes/details/components/breakdown/breakdownBase.tsx @@ -58,6 +58,7 @@ export interface BreakdownStateProps { costOverviewComponent?: React.ReactNode; costType?: string; currency?: string; + dataDetailsComponent?: React.ReactNode; description?: string; detailsURL?: string; emptyStateTitle?: string; @@ -269,6 +270,7 @@ class BreakdownBase extends React.Component { costDistribution, costType, currency, + dataDetailsComponent, description, detailsURL, emptyStateTitle, @@ -316,6 +318,7 @@ class BreakdownBase extends React.Component { : undefined } clusterInfoComponent={clusterInfoComponent} + dataDetailsComponent={dataDetailsComponent} costDistribution={costDistribution} costType={costType} currency={currency} diff --git a/src/routes/details/components/breakdown/breakdownHeader.tsx b/src/routes/details/components/breakdown/breakdownHeader.tsx index 69eacc5ef..7ef5b32a8 100644 --- a/src/routes/details/components/breakdown/breakdownHeader.tsx +++ b/src/routes/details/components/breakdown/breakdownHeader.tsx @@ -33,6 +33,7 @@ interface BreakdownHeaderOwnProps extends RouterComponentProps { costDistribution?: string; costType?: string; currency?: string; + dataDetailsComponent?: React.ReactNode; detailsURL?: string; description?: string; groupBy?: string; @@ -95,6 +96,7 @@ class BreakdownHeader extends React.Component { costDistribution, costType, currency, + dataDetailsComponent, description, groupBy, intl, @@ -158,10 +160,13 @@ class BreakdownHeader extends React.Component { {intl.formatMessage(messages.breakdownTitle, { value: title })} {description && ( - <div> - <span style={styles.description}>{description}</span> - {clusterInfoComponent && isClusterInfoToggleEnabled ? clusterInfoComponent : null} - </div> + <> + <div style={styles.description}> + {description} + {clusterInfoComponent && isClusterInfoToggleEnabled ? clusterInfoComponent : null} + </div> + <div>{dataDetailsComponent && isClusterInfoToggleEnabled ? dataDetailsComponent : null}</div> + </> )} {showCostDistribution && ( diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts index 9ddc0c4e2..630f4feb5 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts @@ -1,6 +1,6 @@ import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; -import global_FontSize_md from '@patternfly/react-tokens/dist/js/global_FontSize_md'; import global_FontSize_xs from '@patternfly/react-tokens/dist/js/global_FontSize_xs'; +import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; import global_warning_color_100 from '@patternfly/react-tokens/dist/js/global_warning_color_100'; import type React from 'react'; @@ -8,15 +8,12 @@ export const styles = { clusterInfo: { fontSize: global_FontSize_xs.value, }, - container: { - overflow: 'auto', - }, loading: { backgroundColor: global_BackgroundColor_light_100.value, minHeight: '520px', }, spacingRight: { - marginRight: global_FontSize_md.value, + marginRight: global_spacer_md.value, }, updateAvailable: { color: global_warning_color_100.value, diff --git a/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx new file mode 100644 index 000000000..d25d2ed18 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx @@ -0,0 +1,172 @@ +import { ProgressStep, ProgressStepper, Text, TextContent, TextVariants } from '@patternfly/react-core'; +import type { Provider } from 'api/providers'; +import { ProviderType } from 'api/providers'; +import type { AxiosError } from 'axios'; +import messages from 'locales/messages'; +import React, { useEffect } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import { NotAvailable } from 'routes/components/page/notAvailable'; +import { LoadingState } from 'routes/components/state/loadingState'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { providersActions, providersSelectors } from 'store/providers'; + +import { styles } from './dataDetails.styles'; +import { getIcon, getVariant, lookupKey } from './utils/status'; + +interface CloudIntegrationOwnProps { + uuid?: string; +} + +interface CloudIntegrationStateProps { + provider: Provider; + providerError: AxiosError; + providerFetchStatus: FetchStatus; + providerQueryString: string; + uuid?: string; +} + +type CloudIntegrationProps = CloudIntegrationOwnProps; + +const CloudIntegration: React.FC = ({ uuid }: CloudIntegrationProps) => { + const intl = useIntl(); + + const { provider, providerError, providerFetchStatus } = useMapToProps({ uuid }); + + const title = intl.formatMessage(messages.optimizations); + + if (providerError) { + return ; + } + + if (providerFetchStatus === FetchStatus.inProgress) { + return ( +
+ +
+ ); + } + + if (!provider) { + return null; + } + + return ( + <> + + {provider?.name || provider?.uuid} + + + + {intl.formatMessage(messages.dataDetailsDownload, { + value: lookupKey(provider.status.download), + })} +
+ {intl.formatDate('2024-03-12T15:00:30.127300Z', { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + })} +
+
+ + {intl.formatMessage(messages.dataDetailsProcessing, { + // value: lookupKey(provider.status.processing), + value: lookupKey('in-progress'), + })} +
+ {intl.formatDate('2024-03-12T15:00:30.127300Z', { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + })} +
+
+ + {intl.formatMessage(messages.dataDetailsSummary, { + // value: lookupKey(provider.status.summary), + value: lookupKey('pending'), + })} +
+ {intl.formatDate('2024-03-12T15:00:30.127300Z', { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + })} +
+
+
+ + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = ({ uuid }): CloudIntegrationStateProps => { + const dispatch: ThunkDispatch = useDispatch(); + + const providerQueryString = uuid; + const provider = useSelector( + (state: RootState) => providersSelectors.selectProviders(state, ProviderType.uuid, providerQueryString) as Provider + ); + const providerError = useSelector((state: RootState) => + providersSelectors.selectProvidersError(state, ProviderType.uuid, providerQueryString) + ); + const providerFetchStatus = useSelector((state: RootState) => + providersSelectors.selectProvidersFetchStatus(state, ProviderType.uuid, providerQueryString) + ); + + useEffect(() => { + if (!providerError && providerFetchStatus !== FetchStatus.inProgress) { + dispatch(providersActions.fetchProviders(ProviderType.uuid, providerQueryString)); + } + }, []); + + return { + provider, + providerError, + providerFetchStatus, + providerQueryString, + }; +}; + +export { CloudIntegration }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts new file mode 100644 index 000000000..e6ede83d2 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts @@ -0,0 +1,30 @@ +import global_BackgroundColor_light_100 from '@patternfly/react-tokens/dist/js/global_BackgroundColor_light_100'; +import global_disabled_color_100 from '@patternfly/react-tokens/dist/js/global_disabled_color_100'; +import global_FontSize_xs from '@patternfly/react-tokens/dist/js/global_FontSize_xs'; +import global_spacer_lg from '@patternfly/react-tokens/dist/js/global_spacer_lg'; +import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; +import type React from 'react'; + +export const styles = { + dataDetails: { + fontSize: global_FontSize_xs.value, + }, + description: { + color: global_disabled_color_100.value, + fontSize: global_FontSize_xs.value, + }, + icon: { + paddingLeft: '7px', + paddingRight: '10px', + }, + loading: { + backgroundColor: global_BackgroundColor_light_100.value, + minHeight: '520px', + }, + spacingRight: { + marginRight: global_spacer_md.value, + }, + stepper: { + margin: global_spacer_lg.value, + }, +} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx new file mode 100644 index 000000000..8eb0272eb --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx @@ -0,0 +1,56 @@ +import { Button, ButtonVariant, Icon } from '@patternfly/react-core'; +import { Modal, ModalBody, ModalHeader, ModalVariant } from '@patternfly/react-core/next'; +import { CheckCircleIcon } from '@patternfly/react-icons/dist/esm/icons/check-circle-icon'; +import messages from 'locales/messages'; +import React, { useState } from 'react'; +import { useIntl } from 'react-intl'; + +import { styles } from './dataDetails.styles'; +import { DataDetailsContent } from './dataDetailsContent'; +import { lookupKey } from './utils/status'; + +interface DataDetailsOwnProps { + clusterId?: string; +} + +type DataDetailsProps = DataDetailsOwnProps; + +const DataDetails: React.FC = ({ clusterId }: DataDetailsProps) => { + const intl = useIntl(); + const [isOpen, setIsOpen] = useState(false); + + const handleOnClose = () => { + setIsOpen(false); + }; + + const handleOnClick = () => { + setIsOpen(!isOpen); + }; + + // PatternFly modal appends to document.body, which is outside the scoped "costManagement" dom tree. + // Use className="costManagement" to override PatternFly styles or append the modal to an element within the tree + + return ( + <> +
+ + + + {intl.formatMessage(messages.dataDetailsSummary, { + value: lookupKey('complete'), // Todo: use status + })} + +
+ + + + + + + + ); +}; + +export default DataDetails; diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx new file mode 100644 index 000000000..79c61a4b5 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx @@ -0,0 +1,176 @@ +import { ProgressStep, ProgressStepper, Text, TextContent, TextVariants } from '@patternfly/react-core'; +import type { Providers } from 'api/providers'; +import { ProviderType } from 'api/providers'; +import { getProvidersQuery } from 'api/queries/providersQuery'; +import type { AxiosError } from 'axios'; +import messages from 'locales/messages'; +import React, { useEffect } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import { NotAvailable } from 'routes/components/page/notAvailable'; +import { LoadingState } from 'routes/components/state/loadingState'; +import { filterProviders } from 'routes/utils/providers'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { providersActions, providersQuery, providersSelectors } from 'store/providers'; + +import { CloudIntegration } from './cloudIntegration'; +import { styles } from './dataDetails.styles'; +import { getIcon, getVariant, lookupKey } from './utils/status'; + +interface DataDetailsContentOwnProps { + clusterId?: string; +} + +interface DataDetailsContentStateProps { + providers: Providers; + providersError: AxiosError; + providersFetchStatus: FetchStatus; + providersQueryString: string; +} + +type DataDetailsContentProps = DataDetailsContentOwnProps; + +const DataDetailsContent: React.FC = ({ clusterId }: DataDetailsContentProps) => { + const intl = useIntl(); + + const { providers, providersError, providersFetchStatus } = useMapToProps(); + + const title = intl.formatMessage(messages.optimizations); + + if (providersError) { + return ; + } + + // Filter OCP providers to skip an extra API request + const ocpProviders = filterProviders(providers, ProviderType.ocp); + const clusterInfo = ocpProviders?.data?.find( + cluster => cluster.authentication?.credentials?.cluster_id === clusterId + ); + + if (providersFetchStatus === FetchStatus.inProgress) { + return ( +
+ +
+ ); + } + + return ( + <> + {clusterInfo?.infrastructure?.uuid && } + + {intl.formatMessage(messages.costData)} + + + + {intl.formatMessage(messages.dataDetailsDownload, { + value: lookupKey(clusterInfo.status.download), + })} +
+ {intl.formatDate('2024-03-12T15:00:30.127300Z', { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + })} +
+
+ + {intl.formatMessage(messages.dataDetailsProcessing, { + value: lookupKey(clusterInfo.status.processing), + })} +
+ {intl.formatDate('2024-03-12T15:00:30.127300Z', { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + })} +
+
+ + {intl.formatMessage(messages.dataDetailsSummary, { + value: lookupKey(clusterInfo.status.summary), + })} +
+ {intl.formatDate('2024-03-12T15:00:30.127300Z', { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + })} +
+
+
+ + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = (): DataDetailsContentStateProps => { + const dispatch: ThunkDispatch = useDispatch(); + + // PermissionsWraper has already made an API request + const providersQueryString = getProvidersQuery(providersQuery); + const providers = useSelector((state: RootState) => + providersSelectors.selectProviders(state, ProviderType.all, providersQueryString) + ); + const providersError = useSelector((state: RootState) => + providersSelectors.selectProvidersError(state, ProviderType.all, providersQueryString) + ); + const providersFetchStatus = useSelector((state: RootState) => + providersSelectors.selectProvidersFetchStatus(state, ProviderType.all, providersQueryString) + ); + + useEffect(() => { + if (!providersError && providersFetchStatus !== FetchStatus.inProgress) { + dispatch(providersActions.fetchProviders(ProviderType.all, providersQueryString)); + } + }, []); + + return { + providers, + providersError, + providersFetchStatus, + providersQueryString, + }; +}; + +export { DataDetailsContent }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/index.ts b/src/routes/details/ocpBreakdown/dataDetails/index.ts new file mode 100644 index 000000000..21ecfb844 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/index.ts @@ -0,0 +1 @@ +export { default as DataDetails } from './dataDetails'; diff --git a/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx b/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx new file mode 100644 index 000000000..429a1b9d9 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx @@ -0,0 +1,25 @@ +import { InProgressIcon } from '@patternfly/react-icons/dist/esm/icons/in-progress-icon'; +import React from 'react'; + +export const lookupKey = value => (value ? value.toLowerCase().replace('-', '_') : undefined); + +export const getIcon = (status: string) => { + const key = lookupKey(status); + if (key === 'in_progress') { + return ; + } + return undefined; +}; + +export const getVariant = (status: string) => { + const key = lookupKey(status); + switch (key) { + case 'complete': + return 'success'; + case 'in_progress': + case 'pending': + return 'pending'; + default: + return 'default'; + } +}; diff --git a/src/routes/details/ocpBreakdown/ocpBreakdown.tsx b/src/routes/details/ocpBreakdown/ocpBreakdown.tsx index 6acf6aa0f..f967485fd 100644 --- a/src/routes/details/ocpBreakdown/ocpBreakdown.tsx +++ b/src/routes/details/ocpBreakdown/ocpBreakdown.tsx @@ -28,6 +28,7 @@ import { getCostDistribution, getCurrency } from 'utils/sessionStorage'; import { ClusterInfo } from './clusterInfo'; import { CostOverview } from './costOverview'; +import { DataDetails } from './dataDetails'; import { HistoricalData } from './historicalData'; import { OcpBreakdownOptimizations } from './ocpBreakdownOptimizations'; @@ -98,6 +99,7 @@ const mapStateToProps = createMapStateToProps : undefined, + dataDetailsComponent: groupBy === 'cluster' ? : undefined, costDistribution, costOverviewComponent: ( Date: Tue, 12 Mar 2024 23:55:02 -0400 Subject: [PATCH 51/61] Show "data details" for breakdown pages https://issues.redhat.com/browse/COST-4761 --- src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx index 8eb0272eb..210bd30ab 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx @@ -44,7 +44,7 @@ const DataDetails: React.FC = ({ clusterId }: DataDetailsProps
- + From 0f87876fd7b66f0c268a7b7581709ecdbcfa2642 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Wed, 13 Mar 2024 13:51:08 -0400 Subject: [PATCH 52/61] Update data details modal per latest feedback https://issues.redhat.com/browse/COST-4761 --- locales/data.json | 343 +++++++++++++++++- locales/translations.json | 17 +- src/api/providers.ts | 1 + src/locales/messages.ts | 127 ++++++- .../clusterInfo/clusterInfo.styles.ts | 1 - .../clusterInfo/clusterInfoContent.tsx | 2 +- .../dataDetails/cloudIntegration.tsx | 45 +-- .../dataDetails/dataDetails.styles.ts | 9 +- .../ocpBreakdown/dataDetails/dataDetails.tsx | 12 +- .../dataDetails/dataDetailsContent.tsx | 49 ++- .../ocpBreakdown/dataDetails/dataStatus.tsx | 86 +++++ .../ocpBreakdown/dataDetails/utils/status.tsx | 25 ++ 12 files changed, 614 insertions(+), 103 deletions(-) create mode 100644 src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx diff --git a/locales/data.json b/locales/data.json index f2e523396..843ef5b84 100644 --- a/locales/data.json +++ b/locales/data.json @@ -665,6 +665,18 @@ "value": "Calculation type" } ], + "calculations": [ + { + "type": 0, + "value": "Calculating Data and Cost Models" + } + ], + "calculationsApplied": [ + { + "type": 0, + "value": "Data correlation and cost models applied" + } + ], "cancel": [ { "type": 0, @@ -997,6 +1009,183 @@ "value": "Cloud integration" } ], + "cloudIntegrationData": [ + { + "type": 0, + "value": "Cloud integration data" + } + ], + "cloudIntegrationDataChecked": [ + { + "options": { + "aws": { + "value": [ + { + "type": 0, + "value": "Checked for data from Amazon Web Services" + } + ] + }, + "azure": { + "value": [ + { + "type": 0, + "value": "Checked for data from Microsoft Azure" + } + ] + }, + "gcp": { + "value": [ + { + "type": 0, + "value": "Checked for data from Google Cloud Platform" + } + ] + }, + "ibm": { + "value": [ + { + "type": 0, + "value": "Checked for data from IBM Cloud" + } + ] + }, + "oci": { + "value": [ + { + "type": 0, + "value": "Checked for data from Oracle Cloud Infrastructure" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "value" + } + ], + "cloudIntegrationDataCheckedAriaLabel": [ + { + "type": 0, + "value": "Checked for data from cloud integration" + } + ], + "cloudIntegrationDataProcessed": [ + { + "options": { + "aws": { + "value": [ + { + "type": 0, + "value": "Data processed from Amazon Web Services" + } + ] + }, + "azure": { + "value": [ + { + "type": 0, + "value": "Data processed from Microsoft Azure" + } + ] + }, + "gcp": { + "value": [ + { + "type": 0, + "value": "Data processed from Google Cloud Platform" + } + ] + }, + "ibm": { + "value": [ + { + "type": 0, + "value": "Data processed from IBM Cloud" + } + ] + }, + "oci": { + "value": [ + { + "type": 0, + "value": "Data processed from Oracle Cloud Infrastructure" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "value" + } + ], + "cloudIntegrationDataProcessedAriaLabel": [ + { + "type": 0, + "value": "Data processed from cloud integration" + } + ], + "cloudIntegrationDataTransferred": [ + { + "options": { + "aws": { + "value": [ + { + "type": 0, + "value": "Data transferred from Amazon Web Services" + } + ] + }, + "azure": { + "value": [ + { + "type": 0, + "value": "Data transferred from Microsoft Azure" + } + ] + }, + "gcp": { + "value": [ + { + "type": 0, + "value": "Data transferred from Google Cloud Platform" + } + ] + }, + "ibm": { + "value": [ + { + "type": 0, + "value": "Data transferred from IBM Cloud" + } + ] + }, + "oci": { + "value": [ + { + "type": 0, + "value": "Data transferred from Oracle Cloud Infrastructure" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "value" + } + ], + "cloudIntegrationDataTransferredAriaLabel": [ + { + "type": 0, + "value": "Data transferred from cloud integration" + } + ], "cluster": [ { "type": 0, @@ -1093,12 +1282,6 @@ "value": "Cost categories" } ], - "costData": [ - { - "type": 0, - "value": "Cost data" - } - ], "costDistribution": [ { "type": 0, @@ -1162,12 +1345,6 @@ "value": "Cost Management" } ], - "costManagementOperatorVersion": [ - { - "type": 0, - "value": "Cost Management operator version" - } - ], "costModels": [ { "type": 0, @@ -10346,6 +10523,142 @@ "value": "value" } ], + "metricsOperatorData": [ + { + "type": 0, + "value": "Cost Management metrics operator data" + } + ], + "metricsOperatorDataProcessed": [ + { + "options": { + "aws": { + "value": [ + { + "type": 0, + "value": "Data processed from Amazon Web Services" + } + ] + }, + "azure": { + "value": [ + { + "type": 0, + "value": "Data processed from Microsoft Azure" + } + ] + }, + "gcp": { + "value": [ + { + "type": 0, + "value": "Data processed from Google Cloud Platform" + } + ] + }, + "ibm": { + "value": [ + { + "type": 0, + "value": "Data processed from IBM Cloud" + } + ] + }, + "oci": { + "value": [ + { + "type": 0, + "value": "Data processed from Oracle Cloud Infrastructure" + } + ] + }, + "ocp": { + "value": [ + { + "type": 0, + "value": "Data processed from OpenShift" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "value" + } + ], + "metricsOperatorDataProcessedAriaLabel": [ + { + "type": 0, + "value": "Data received from cloud integration" + } + ], + "metricsOperatorDataReceived": [ + { + "options": { + "aws": { + "value": [ + { + "type": 0, + "value": "Data received from Amazon Web Services" + } + ] + }, + "azure": { + "value": [ + { + "type": 0, + "value": "Data received from Microsoft Azure" + } + ] + }, + "gcp": { + "value": [ + { + "type": 0, + "value": "Data received from Google Cloud Platform" + } + ] + }, + "ibm": { + "value": [ + { + "type": 0, + "value": "Data received from IBM Cloud" + } + ] + }, + "oci": { + "value": [ + { + "type": 0, + "value": "Data received from Oracle Cloud Infrastructure" + } + ] + }, + "ocp": { + "value": [ + { + "type": 0, + "value": "Data received from OpenShift" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "value" + } + ], + "metricsOperatorVersion": [ + { + "type": 0, + "value": "Cost Management operator version" + } + ], "monthOverMonthChange": [ { "type": 0, @@ -12852,12 +13165,6 @@ "value": "units" } ], - "unknown": [ - { - "type": 0, - "value": "Unknown" - } - ], "updateAvailable": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 4b850be93..40b80c487 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -30,6 +30,8 @@ "breakdownTitle": "{value}", "breakdownTotalCostDate": "{value} total cost ({dateRange})", "calculationType": "Calculation type", + "calculations": "Calculating Data and Cost Models", + "calculationsApplied": "Data correlation and cost models applied", "cancel": "Cancel", "chartCostForecastConeLegendLabel": "Cost confidence ({dateRange})", "chartCostForecastConeLegendNoDataLabel": "Cost confidence (no data)", @@ -60,6 +62,13 @@ "chooseValuePlaceholder": "Choose value", "close": "Close", "cloudIntegration": "Cloud integration", + "cloudIntegrationData": "Cloud integration data", + "cloudIntegrationDataChecked": "{value, select, aws {Checked for data from Amazon Web Services} azure {Checked for data from Microsoft Azure} oci {Checked for data from Oracle Cloud Infrastructure} gcp {Checked for data from Google Cloud Platform} ibm {Checked for data from IBM Cloud} other {}}", + "cloudIntegrationDataCheckedAriaLabel": "Checked for data from cloud integration", + "cloudIntegrationDataProcessed": "{value, select, aws {Data processed from Amazon Web Services} azure {Data processed from Microsoft Azure} oci {Data processed from Oracle Cloud Infrastructure} gcp {Data processed from Google Cloud Platform} ibm {Data processed from IBM Cloud} other {}}", + "cloudIntegrationDataProcessedAriaLabel": "Data processed from cloud integration", + "cloudIntegrationDataTransferred": "{value, select, aws {Data transferred from Amazon Web Services} azure {Data transferred from Microsoft Azure} oci {Data transferred from Oracle Cloud Infrastructure} gcp {Data transferred from Google Cloud Platform} ibm {Data transferred from IBM Cloud} other {}}", + "cloudIntegrationDataTransferredAriaLabel": "Data transferred from cloud integration", "cluster": "Cluster", "clusterId": "Cluster id", "clusterInfo": "Cluster information", @@ -74,7 +83,6 @@ "costCategoryDesc": "Enable your AWS cost categories to be used for report grouping and filtering. Changes will be reflected within 24 hours. {learnMore}", "costCategoryNames": "Cost category names", "costCategoryTitle": "Cost categories", - "costData": "Cost data", "costDistribution": "Cost distribution", "costDistributionAriaDesc": "Overhead cost breakdown of platform, worker unallocated, and total costs", "costDistributionAriaLabel": "A description of platform, worker unallocated, and total costs", @@ -82,7 +90,6 @@ "costDistributionTitle": "Overhead cost breakdown", "costDistributionType": "{value, select, distributed {Distribute through cost models} total {Don't distribute overhead costs} other {}}", "costManagement": "Cost Management", - "costManagementOperatorVersion": "Cost Management operator version", "costModels": "Cost Models", "costModelsActions": "Cost model actions", "costModelsAddTagValues": "Add more tag values", @@ -373,6 +380,11 @@ "metric": "Metric", "metricPlaceholder": "Filter by metrics", "metricValues": "{value, select, cpu {CPU} cluster {Cluster} memory {Memory} node {Node} persistent_volume_claims {Persistent volume claims} storage {Storage} other {}}", + "metricsOperatorData": "Cost Management metrics operator data", + "metricsOperatorDataProcessed": "{value, select, aws {Data processed from Amazon Web Services} azure {Data processed from Microsoft Azure} oci {Data processed from Oracle Cloud Infrastructure} gcp {Data processed from Google Cloud Platform} ibm {Data processed from IBM Cloud} ocp {Data processed from OpenShift} other {}}", + "metricsOperatorDataProcessedAriaLabel": "Data received from cloud integration", + "metricsOperatorDataReceived": "{value, select, aws {Data received from Amazon Web Services} azure {Data received from Microsoft Azure} oci {Data received from Oracle Cloud Infrastructure} gcp {Data received from Google Cloud Platform} ibm {Data received from IBM Cloud} ocp {Data received from OpenShift} other {}}", + "metricsOperatorVersion": "Cost Management operator version", "monthOverMonthChange": "Month over month change", "names": "{count, plural, one {Name} other {Names}}", "next": "next", @@ -586,7 +598,6 @@ "typeaheadAriaClear": "Clear button and input", "unitTooltips": "{units, select, byte_ms {{value} Byte-ms} cluster_month {cluster-month} core_hours {{value} core-hours} gb {{value} GB} gb_hours {{value} GB-hours} gb_month {{value} GB-month} gb_ms {{value} GB-ms} gib_hours {{value} GiB-hours} gib_month {{value} GiB-month} gibibyte_month {{value} GiB-month} hour {{value} hours} hrs {{value} hours} ms {{value} milliseconds} pvc_month {PVC-month} tag_month {{value} tag-month} vm_hours {{value} VM-hours} other {{value}}}", "units": "{units, select, byte_ms {Byte-ms} cluster_month {cluster-month} core {core} core_hours {core-hours} gb {GB} gb_hours {GB-hours} gb_month {GB-month} gb_ms {GB-ms} gib_hours {GiB-hours} gib_month {GiB-month} gibibyte_month {GiB-month} hour {hours} hrs {hours} ms {milliseconds} pvc_month {PVC-month} tag_month {tag-month} vm_hours {VM-hours} other {}}", - "unknown": "Unknown", "updateAvailable": "Update available", "usage": "Usage", "usageCostDesc": "The portion of cost calculated by applying hourly and/or monthly price list rates to metrics.", diff --git a/src/api/providers.ts b/src/api/providers.ts index 2484b2127..7ebfd273d 100644 --- a/src/api/providers.ts +++ b/src/api/providers.ts @@ -58,6 +58,7 @@ export interface Provider { has_data?: boolean; id?: string; infrastructure?: ProviderInfrastructure; + last_payload_received_at?: string; name?: string; paused?: boolean; previous_month_data?: boolean; diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 0eb0609f5..80d1846c6 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -200,6 +200,16 @@ export default defineMessages({ description: 'Calculation type', id: 'calculationType', }, + calculations: { + defaultMessage: 'Calculating Data and Cost Models', + description: 'Calculating Data and Cost Models', + id: 'calculations', + }, + calculationsApplied: { + defaultMessage: 'Data correlation and cost models applied', + description: 'Data correlation and cost models applied', + id: 'calculationsApplied', + }, cancel: { defaultMessage: 'Cancel', description: 'Cancel', @@ -350,6 +360,62 @@ export default defineMessages({ description: 'Cloud integration', id: 'cloudIntegration', }, + cloudIntegrationData: { + defaultMessage: 'Cloud integration data', + description: 'Cloud integration data', + id: 'cloudIntegrationData', + }, + cloudIntegrationDataChecked: { + defaultMessage: + '{value, select, ' + + 'aws {Checked for data from Amazon Web Services} ' + + 'azure {Checked for data from Microsoft Azure} ' + + 'oci {Checked for data from Oracle Cloud Infrastructure} ' + + 'gcp {Checked for data from Google Cloud Platform} ' + + 'ibm {Checked for data from IBM Cloud} ' + + 'other {}}', + description: 'Checked for data from cloud integration', + id: 'cloudIntegrationDataChecked', + }, + cloudIntegrationDataCheckedAriaLabel: { + defaultMessage: 'Checked for data from cloud integration', + description: 'Check for data from cloud integration', + id: 'cloudIntegrationDataCheckedAriaLabel', + }, + cloudIntegrationDataProcessed: { + defaultMessage: + '{value, select, ' + + 'aws {Data processed from Amazon Web Services} ' + + 'azure {Data processed from Microsoft Azure} ' + + 'oci {Data processed from Oracle Cloud Infrastructure} ' + + 'gcp {Data processed from Google Cloud Platform} ' + + 'ibm {Data processed from IBM Cloud} ' + + 'other {}}', + description: 'Data processed from cloud integration', + id: 'cloudIntegrationDataProcessed', + }, + cloudIntegrationDataProcessedAriaLabel: { + defaultMessage: 'Data processed from cloud integration', + description: 'Data processed from cloud integration', + id: 'cloudIntegrationDataProcessedAriaLabel', + }, + cloudIntegrationDataTransferred: { + defaultMessage: + '{value, select, ' + + 'aws {Data transferred from Amazon Web Services} ' + + 'azure {Data transferred from Microsoft Azure} ' + + 'oci {Data transferred from Oracle Cloud Infrastructure} ' + + 'gcp {Data transferred from Google Cloud Platform} ' + + 'ibm {Data transferred from IBM Cloud} ' + + 'other {}}', + description: 'Data transferred from cloud integration', + id: 'cloudIntegrationDataTransferred', + }, + cloudIntegrationDataTransferredAriaLabel: { + defaultMessage: 'Data transferred from cloud integration', + description: 'Data transferred from cloud integration', + id: 'cloudIntegrationDataTransferredAriaLabel', + }, cluster: { defaultMessage: 'Cluster', description: 'Cluster', @@ -422,11 +488,6 @@ export default defineMessages({ description: 'Cost categories', id: 'costCategoryTitle', }, - costData: { - defaultMessage: 'Cost data', - description: 'Cost data', - id: 'costData', - }, costDistribution: { defaultMessage: 'Cost distribution', description: 'Cost distribution', @@ -466,11 +527,6 @@ export default defineMessages({ description: 'Cost Management', id: 'costManagement', }, - costManagementOperatorVersion: { - defaultMessage: 'Cost Management operator version', - description: 'Cost Management operator version', - id: 'costManagementOperatorVersion', - }, costModels: { defaultMessage: 'Cost Models', description: 'Cost Models', @@ -2437,6 +2493,52 @@ export default defineMessages({ description: 'Metric values', id: 'metricValues', }, + metricsOperatorData: { + defaultMessage: 'Cost Management metrics operator data', + description: 'Cost Management metrics operator data', + id: 'metricsOperatorData', + }, + metricsOperatorDataProcessed: { + defaultMessage: + '{value, select, ' + + 'aws {Data processed from Amazon Web Services} ' + + 'azure {Data processed from Microsoft Azure} ' + + 'oci {Data processed from Oracle Cloud Infrastructure} ' + + 'gcp {Data processed from Google Cloud Platform} ' + + 'ibm {Data processed from IBM Cloud} ' + + 'ocp {Data processed from OpenShift} ' + + 'other {}}', + description: 'Data processed from cloud integration', + id: 'metricsOperatorDataProcessed', + }, + metricsOperatorDataProcessedAriaLabel: { + defaultMessage: 'Data processed from cloud integration', + description: 'Data processed from cloud integration', + id: 'metricsOperatorDataProcessedAriaLabel', + }, + metricsOperatorDataReceived: { + defaultMessage: + '{value, select, ' + + 'aws {Data received from Amazon Web Services} ' + + 'azure {Data received from Microsoft Azure} ' + + 'oci {Data received from Oracle Cloud Infrastructure} ' + + 'gcp {Data received from Google Cloud Platform} ' + + 'ibm {Data received from IBM Cloud} ' + + 'ocp {Data received from OpenShift} ' + + 'other {}}', + description: 'Data received from cloud integration', + id: 'metricsOperatorDataReceived', + }, + metricsOperatorDataReceivedAriaLabel: { + defaultMessage: 'Data received from cloud integration', + description: 'Data received from cloud integration', + id: 'metricsOperatorDataProcessedAriaLabel', + }, + metricsOperatorVersion: { + defaultMessage: 'Cost Management operator version', + description: 'Cost Management operator version', + id: 'metricsOperatorVersion', + }, monthOverMonthChange: { defaultMessage: 'Month over month change', description: 'Month over month change', @@ -3632,11 +3734,6 @@ export default defineMessages({ description: 'return the proper unit label based on key: "units"', id: 'units', }, - unknown: { - defaultMessage: 'Unknown', - description: 'Unknown', - id: 'unknown', - }, updateAvailable: { defaultMessage: 'Update available', description: 'Update available', diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts index 630f4feb5..a82b201a2 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts @@ -10,7 +10,6 @@ export const styles = { }, loading: { backgroundColor: global_BackgroundColor_light_100.value, - minHeight: '520px', }, spacingRight: { marginRight: global_spacer_md.value, diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx index 83796f3f5..463362262 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx @@ -77,7 +77,7 @@ const ClusterInfoContent: React.FC = ({ clusterId }: Cl )} - {intl.formatMessage(messages.costManagementOperatorVersion)} + {intl.formatMessage(messages.metricsOperatorVersion)} {clusterInfo?.additional_context?.operator_version} diff --git a/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx index d25d2ed18..17720a975 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx @@ -57,25 +57,20 @@ const CloudIntegration: React.FC = ({ uuid }: CloudIntegr return ( <> - {provider?.name || provider?.uuid} + {intl.formatMessage(messages.cloudIntegrationData)} - + - {intl.formatMessage(messages.dataDetailsDownload, { - value: lookupKey(provider.status.download), + {intl.formatMessage(messages.cloudIntegrationDataChecked, { + value: lookupKey(provider.source_type), })}
- {intl.formatDate('2024-03-12T15:00:30.127300Z', { + {intl.formatDate(provider.last_payload_received_at, { day: 'numeric', hour: 'numeric', hour12: false, @@ -88,18 +83,17 @@ const CloudIntegration: React.FC = ({ uuid }: CloudIntegr
- {intl.formatMessage(messages.dataDetailsProcessing, { - // value: lookupKey(provider.status.processing), - value: lookupKey('in-progress'), + {intl.formatMessage(messages.cloudIntegrationDataTransferred, { + value: lookupKey(provider.source_type), })}
- {intl.formatDate('2024-03-12T15:00:30.127300Z', { + {intl.formatDate(provider.last_payload_received_at, { day: 'numeric', hour: 'numeric', hour12: false, @@ -112,18 +106,17 @@ const CloudIntegration: React.FC = ({ uuid }: CloudIntegr
- {intl.formatMessage(messages.dataDetailsSummary, { - // value: lookupKey(provider.status.summary), - value: lookupKey('pending'), + {intl.formatMessage(messages.cloudIntegrationDataProcessed, { + value: lookupKey(provider.source_type), })}
- {intl.formatDate('2024-03-12T15:00:30.127300Z', { + {intl.formatDate(provider.last_payload_received_at, { day: 'numeric', hour: 'numeric', hour12: false, diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts index e6ede83d2..d661596bb 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts @@ -13,17 +13,16 @@ export const styles = { color: global_disabled_color_100.value, fontSize: global_FontSize_xs.value, }, - icon: { - paddingLeft: '7px', - paddingRight: '10px', - }, loading: { backgroundColor: global_BackgroundColor_light_100.value, - minHeight: '520px', }, spacingRight: { marginRight: global_spacer_md.value, }, + statusIcon: { + paddingLeft: '1px', + paddingRight: '5px', + }, stepper: { margin: global_spacer_lg.value, }, diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx index 210bd30ab..cedd95bf0 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx @@ -1,13 +1,12 @@ -import { Button, ButtonVariant, Icon } from '@patternfly/react-core'; +import { Button, ButtonVariant } from '@patternfly/react-core'; import { Modal, ModalBody, ModalHeader, ModalVariant } from '@patternfly/react-core/next'; -import { CheckCircleIcon } from '@patternfly/react-icons/dist/esm/icons/check-circle-icon'; import messages from 'locales/messages'; import React, { useState } from 'react'; import { useIntl } from 'react-intl'; import { styles } from './dataDetails.styles'; import { DataDetailsContent } from './dataDetailsContent'; -import { lookupKey } from './utils/status'; +import { DataStatus } from './dataStatus'; interface DataDetailsOwnProps { clusterId?: string; @@ -33,12 +32,7 @@ const DataDetails: React.FC = ({ clusterId }: DataDetailsProps return ( <>
- - - - {intl.formatMessage(messages.dataDetailsSummary, { - value: lookupKey('complete'), // Todo: use status - })} + diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx index 79c61a4b5..f3391d79b 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx @@ -18,7 +18,7 @@ import { providersActions, providersQuery, providersSelectors } from 'store/prov import { CloudIntegration } from './cloudIntegration'; import { styles } from './dataDetails.styles'; -import { getIcon, getVariant, lookupKey } from './utils/status'; +import { getProgressIcon, getVariant, lookupKey } from './utils/status'; interface DataDetailsContentOwnProps { clusterId?: string; @@ -62,25 +62,21 @@ const DataDetailsContent: React.FC = ({ clusterId }: Da <> {clusterInfo?.infrastructure?.uuid && } - {intl.formatMessage(messages.costData)} + {intl.formatMessage(messages.metricsOperatorData)} - + - {intl.formatMessage(messages.dataDetailsDownload, { - value: lookupKey(clusterInfo.status.download), + {intl.formatMessage(messages.metricsOperatorDataReceived, { + value: lookupKey(clusterInfo.source_type), })}
- {intl.formatDate('2024-03-12T15:00:30.127300Z', { + {intl.formatDate(clusterInfo.last_payload_received_at, { day: 'numeric', hour: 'numeric', hour12: false, @@ -93,17 +89,17 @@ const DataDetailsContent: React.FC = ({ clusterId }: Da
- {intl.formatMessage(messages.dataDetailsProcessing, { - value: lookupKey(clusterInfo.status.processing), + {intl.formatMessage(messages.metricsOperatorDataProcessed, { + value: lookupKey(clusterInfo.source_type), })}
- {intl.formatDate('2024-03-12T15:00:30.127300Z', { + {intl.formatDate(clusterInfo.last_payload_received_at, { day: 'numeric', hour: 'numeric', hour12: false, @@ -115,18 +111,21 @@ const DataDetailsContent: React.FC = ({ clusterId }: Da })}
+
+ + {intl.formatMessage(messages.calculations)} + + - {intl.formatMessage(messages.dataDetailsSummary, { - value: lookupKey(clusterInfo.status.summary), - })} + {intl.formatMessage(messages.calculationsApplied)}
- {intl.formatDate('2024-03-12T15:00:30.127300Z', { + {intl.formatDate(clusterInfo.last_payload_received_at, { day: 'numeric', hour: 'numeric', hour12: false, diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx new file mode 100644 index 000000000..93e615ac5 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx @@ -0,0 +1,86 @@ +import type { Providers } from 'api/providers'; +import { ProviderType } from 'api/providers'; +import { getProvidersQuery } from 'api/queries/providersQuery'; +import type { AxiosError } from 'axios/index'; +import messages from 'locales/messages'; +import React, { useEffect } from 'react'; +import { useIntl } from 'react-intl'; +import { useDispatch, useSelector } from 'react-redux'; +import type { AnyAction } from 'redux'; +import type { ThunkDispatch } from 'redux-thunk'; +import { filterProviders } from 'routes/utils/providers'; +import type { RootState } from 'store'; +import { FetchStatus } from 'store/common'; +import { providersActions, providersQuery, providersSelectors } from 'store/providers'; + +import { styles } from './dataDetails.styles'; +import { getIcon, lookupKey } from './utils/status'; + +interface DataStatusOwnProps { + clusterId?: string; +} + +interface DataStatusStateProps { + providers: Providers; + providersError: AxiosError; + providersFetchStatus: FetchStatus; + providersQueryString: string; +} + +type DataStatusProps = DataStatusOwnProps; + +const DataStatus: React.FC = ({ clusterId }: DataStatusProps) => { + const intl = useIntl(); + const { providers, providersError } = useMapToProps(); + + if (!providers || providersError) { + return null; + } + + // Filter OCP providers to skip an extra API request + const ocpProviders = filterProviders(providers, ProviderType.ocp); + const clusterInfo = ocpProviders?.data?.find( + cluster => cluster.authentication?.credentials?.cluster_id === clusterId + ); + + return ( + <> + {getIcon(clusterInfo?.status?.summary)} + {intl.formatMessage(messages.dataDetailsSummary, { + value: lookupKey(clusterInfo?.status?.summary), + })} + + ); +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = (): DataStatusStateProps => { + const dispatch: ThunkDispatch = useDispatch(); + + // PermissionsWraper has already made an API request + const providersQueryString = getProvidersQuery(providersQuery); + const providers = useSelector((state: RootState) => + providersSelectors.selectProviders(state, ProviderType.all, providersQueryString) + ); + const providersError = useSelector((state: RootState) => + providersSelectors.selectProvidersError(state, ProviderType.all, providersQueryString) + ); + const providersFetchStatus = useSelector((state: RootState) => + providersSelectors.selectProvidersFetchStatus(state, ProviderType.all, providersQueryString) + ); + + useEffect(() => { + if (!providersError && providersFetchStatus !== FetchStatus.inProgress) { + dispatch(providersActions.fetchProviders(ProviderType.all, providersQueryString)); + } + }, []); + + return { + providers, + providersError, + providersFetchStatus, + providersQueryString, + }; +}; + +export { DataStatus }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx b/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx index 429a1b9d9..2f74271f8 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx @@ -1,9 +1,34 @@ +import { Icon } from '@patternfly/react-core'; +import { CheckCircleIcon } from '@patternfly/react-icons/dist/esm/icons/check-circle-icon'; +import { CircleIcon } from '@patternfly/react-icons/dist/esm/icons/circle-icon'; import { InProgressIcon } from '@patternfly/react-icons/dist/esm/icons/in-progress-icon'; import React from 'react'; export const lookupKey = value => (value ? value.toLowerCase().replace('-', '_') : undefined); export const getIcon = (status: string) => { + let icon; + let variant; + + switch (lookupKey(status)) { + case 'complete': + icon = ; + variant = 'success'; + break; + case 'in_progress': + icon = ; + break; + case 'pending': + icon = ; + break; + default: + icon = null; + break; + } + return icon ? {icon} : null; +}; + +export const getProgressIcon = (status: string) => { const key = lookupKey(status); if (key === 'in_progress') { return ; From eea8ebe43a2fc3133b23cc7d2b5ee7812cc86271 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Wed, 13 Mar 2024 14:19:59 -0400 Subject: [PATCH 53/61] Remove unused messages --- locales/data.json | 121 ------------------ locales/translations.json | 5 - src/locales/messages.ts | 40 ------ .../ocpBreakdown/dataDetails/dataStatus.tsx | 2 +- 4 files changed, 1 insertion(+), 167 deletions(-) diff --git a/locales/data.json b/locales/data.json index 843ef5b84..c05efd0d3 100644 --- a/locales/data.json +++ b/locales/data.json @@ -2557,127 +2557,6 @@ "value": "Data details" } ], - "dataDetailsDownload": [ - { - "options": { - "complete": { - "value": [ - { - "type": 0, - "value": "Data download complete" - } - ] - }, - "in_progress": { - "value": [ - { - "type": 0, - "value": "Data download in progress" - } - ] - }, - "other": { - "value": [] - }, - "pending": { - "value": [ - { - "type": 0, - "value": "Data download pending" - } - ] - } - }, - "type": 5, - "value": "value" - } - ], - "dataDetailsProcessing": [ - { - "options": { - "complete": { - "value": [ - { - "type": 0, - "value": "Data processing complete" - } - ] - }, - "in_progress": { - "value": [ - { - "type": 0, - "value": "Data processing in progress" - } - ] - }, - "other": { - "value": [] - }, - "pending": { - "value": [ - { - "type": 0, - "value": "Data processing pending" - } - ] - } - }, - "type": 5, - "value": "value" - } - ], - "dataDetailsProgressStep": [ - { - "type": 0, - "value": "Data details progress step " - }, - { - "type": 1, - "value": "count" - } - ], - "dataDetailsProgressStepper": [ - { - "type": 0, - "value": "Data details progress stepper" - } - ], - "dataDetailsSummary": [ - { - "options": { - "complete": { - "value": [ - { - "type": 0, - "value": "Data summary complete" - } - ] - }, - "in_progress": { - "value": [ - { - "type": 0, - "value": "Data summary in progress" - } - ] - }, - "other": { - "value": [] - }, - "pending": { - "value": [ - { - "type": 0, - "value": "Data summary pending" - } - ] - } - }, - "type": 5, - "value": "value" - } - ], "dataTableAriaLabel": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 40b80c487..ce6792ab1 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -206,11 +206,6 @@ "dashboardStorageTitle": "Storage services usage", "dashboardTotalCostTooltip": "This total cost is the sum of the infrastructure cost {infrastructureCost} and supplementary cost {supplementaryCost}", "dataDetails": "Data details", - "dataDetailsDownload": "{value, select, complete {Data download complete}in_progress {Data download in progress}pending {Data download pending}other {}}", - "dataDetailsProcessing": "{value, select, complete {Data processing complete}in_progress {Data processing in progress}pending {Data processing pending}other {}}", - "dataDetailsProgressStep": "Data details progress step {count}", - "dataDetailsProgressStepper": "Data details progress stepper", - "dataDetailsSummary": "{value, select, complete {Data summary complete}in_progress {Data summary in progress}pending {Data summary pending}other {}}", "dataTableAriaLabel": "Details table", "datePickerAfterError": "Date is after the allowable range", "datePickerBeforeError": "Date is before the allowable range", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index 80d1846c6..c9fadb216 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -1183,46 +1183,6 @@ export default defineMessages({ description: 'Data details', id: 'dataDetails', }, - dataDetailsDownload: { - defaultMessage: - '{value, select, ' + - 'complete {Data download complete}' + - 'in_progress {Data download in progress}' + - 'pending {Data download pending}' + - 'other {}}', - description: 'Data download status', - id: 'dataDetailsDownload', - }, - dataDetailsProcessing: { - defaultMessage: - '{value, select, ' + - 'complete {Data processing complete}' + - 'in_progress {Data processing in progress}' + - 'pending {Data processing pending}' + - 'other {}}', - description: 'Data processing status', - id: 'dataDetailsProcessing', - }, - dataDetailsProgressStep: { - defaultMessage: 'Data details progress step {count}', - description: 'Data details progress step {count}', - id: 'dataDetailsProgressStep', - }, - dataDetailsProgressStepper: { - defaultMessage: 'Data details progress stepper', - description: 'Data details progress stepper', - id: 'dataDetailsProgressStepper', - }, - dataDetailsSummary: { - defaultMessage: - '{value, select, ' + - 'complete {Data summary complete}' + - 'in_progress {Data summary in progress}' + - 'pending {Data summary pending}' + - 'other {}}', - description: 'Data summary status', - id: 'dataDetailsSummary', - }, dataTableAriaLabel: { defaultMessage: 'Details table', description: 'Details table', diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx index 93e615ac5..a03346754 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx @@ -46,7 +46,7 @@ const DataStatus: React.FC = ({ clusterId }: DataStatusProps) = return ( <> {getIcon(clusterInfo?.status?.summary)} - {intl.formatMessage(messages.dataDetailsSummary, { + {intl.formatMessage(messages.calculationsApplied, { value: lookupKey(clusterInfo?.status?.summary), })} From 2588782f517997c9bdcb05d0d05f86e45aade4f3 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Fri, 15 Mar 2024 10:16:15 -0400 Subject: [PATCH 54/61] Added integrations steps --- locales/data.json | 745 ++++++++---------- locales/translations.json | 24 +- src/api/providers.ts | 22 +- .../inactiveSources/inactiveSources.tsx | 2 +- src/locales/messages.ts | 195 +++-- .../breakdown/breakdownHeader.styles.ts | 1 + .../components/breakdown/breakdownHeader.tsx | 16 +- .../clusterInfo/clusterInfoContent.tsx | 1 - .../dataDetails/cloudIntegration.tsx | 40 +- .../dataDetails/dataDetails.styles.ts | 4 +- .../ocpBreakdown/dataDetails/dataDetails.tsx | 2 +- .../dataDetails/dataDetailsContent.tsx | 181 +++-- .../ocpBreakdown/dataDetails/dataStatus.tsx | 60 +- .../ocpBreakdown/dataDetails/utils/status.tsx | 152 +++- 14 files changed, 772 insertions(+), 673 deletions(-) diff --git a/locales/data.json b/locales/data.json index c05efd0d3..8d6c3093a 100644 --- a/locales/data.json +++ b/locales/data.json @@ -665,18 +665,6 @@ "value": "Calculation type" } ], - "calculations": [ - { - "type": 0, - "value": "Calculating Data and Cost Models" - } - ], - "calculationsApplied": [ - { - "type": 0, - "value": "Data correlation and cost models applied" - } - ], "cancel": [ { "type": 0, @@ -1009,183 +997,6 @@ "value": "Cloud integration" } ], - "cloudIntegrationData": [ - { - "type": 0, - "value": "Cloud integration data" - } - ], - "cloudIntegrationDataChecked": [ - { - "options": { - "aws": { - "value": [ - { - "type": 0, - "value": "Checked for data from Amazon Web Services" - } - ] - }, - "azure": { - "value": [ - { - "type": 0, - "value": "Checked for data from Microsoft Azure" - } - ] - }, - "gcp": { - "value": [ - { - "type": 0, - "value": "Checked for data from Google Cloud Platform" - } - ] - }, - "ibm": { - "value": [ - { - "type": 0, - "value": "Checked for data from IBM Cloud" - } - ] - }, - "oci": { - "value": [ - { - "type": 0, - "value": "Checked for data from Oracle Cloud Infrastructure" - } - ] - }, - "other": { - "value": [] - } - }, - "type": 5, - "value": "value" - } - ], - "cloudIntegrationDataCheckedAriaLabel": [ - { - "type": 0, - "value": "Checked for data from cloud integration" - } - ], - "cloudIntegrationDataProcessed": [ - { - "options": { - "aws": { - "value": [ - { - "type": 0, - "value": "Data processed from Amazon Web Services" - } - ] - }, - "azure": { - "value": [ - { - "type": 0, - "value": "Data processed from Microsoft Azure" - } - ] - }, - "gcp": { - "value": [ - { - "type": 0, - "value": "Data processed from Google Cloud Platform" - } - ] - }, - "ibm": { - "value": [ - { - "type": 0, - "value": "Data processed from IBM Cloud" - } - ] - }, - "oci": { - "value": [ - { - "type": 0, - "value": "Data processed from Oracle Cloud Infrastructure" - } - ] - }, - "other": { - "value": [] - } - }, - "type": 5, - "value": "value" - } - ], - "cloudIntegrationDataProcessedAriaLabel": [ - { - "type": 0, - "value": "Data processed from cloud integration" - } - ], - "cloudIntegrationDataTransferred": [ - { - "options": { - "aws": { - "value": [ - { - "type": 0, - "value": "Data transferred from Amazon Web Services" - } - ] - }, - "azure": { - "value": [ - { - "type": 0, - "value": "Data transferred from Microsoft Azure" - } - ] - }, - "gcp": { - "value": [ - { - "type": 0, - "value": "Data transferred from Google Cloud Platform" - } - ] - }, - "ibm": { - "value": [ - { - "type": 0, - "value": "Data transferred from IBM Cloud" - } - ] - }, - "oci": { - "value": [ - { - "type": 0, - "value": "Data transferred from Oracle Cloud Infrastructure" - } - ] - }, - "other": { - "value": [] - } - }, - "type": 5, - "value": "value" - } - ], - "cloudIntegrationDataTransferredAriaLabel": [ - { - "type": 0, - "value": "Data transferred from cloud integration" - } - ], "cluster": [ { "type": 0, @@ -2435,47 +2246,322 @@ "value": [ { "type": 0, - "value": "NOK" + "value": "NOK" + } + ] + }, + "NZD": { + "value": [ + { + "type": 0, + "value": "NZ$" + } + ] + }, + "SEK": { + "value": [ + { + "type": 0, + "value": "SEK" + } + ] + }, + "SGD": { + "value": [ + { + "type": 0, + "value": "SGD" + } + ] + }, + "USD": { + "value": [ + { + "type": 0, + "value": "$" + } + ] + }, + "ZAR": { + "value": [ + { + "type": 0, + "value": "ZAR" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "units" + } + ], + "dashboardCumulativeCostComparison": [ + { + "type": 0, + "value": "Cumulative cost comparison (" + }, + { + "type": 1, + "value": "units" + }, + { + "type": 0, + "value": ")" + } + ], + "dashboardDailyUsageComparison": [ + { + "type": 0, + "value": "Daily usage comparison (" + }, + { + "type": 1, + "value": "units" + }, + { + "type": 0, + "value": ")" + } + ], + "dashboardDatabaseTitle": [ + { + "type": 0, + "value": "Database services cost" + } + ], + "dashboardNetworkTitle": [ + { + "type": 0, + "value": "Network services cost" + } + ], + "dashboardStorageTitle": [ + { + "type": 0, + "value": "Storage services usage" + } + ], + "dashboardTotalCostTooltip": [ + { + "type": 0, + "value": "This total cost is the sum of the infrastructure cost " + }, + { + "type": 1, + "value": "infrastructureCost" + }, + { + "type": 0, + "value": " and supplementary cost " + }, + { + "type": 1, + "value": "supplementaryCost" + } + ], + "dataChecked": [ + { + "options": { + "aws": { + "value": [ + { + "type": 0, + "value": "Checked for data from Amazon Web Services" + } + ] + }, + "azure": { + "value": [ + { + "type": 0, + "value": "Checked for data from Microsoft Azure" + } + ] + }, + "gcp": { + "value": [ + { + "type": 0, + "value": "Checked for data from Google Cloud Platform" + } + ] + }, + "ibm": { + "value": [ + { + "type": 0, + "value": "Checked for data from IBM Cloud" + } + ] + }, + "oci": { + "value": [ + { + "type": 0, + "value": "Checked for data from Oracle Cloud Infrastructure" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "value" + } + ], + "dataCheckedAriaLabel": [ + { + "type": 0, + "value": "Checked for data from cloud integration" + } + ], + "dataDetails": [ + { + "type": 0, + "value": "Data details" + } + ], + "dataDetailsCalculated": [ + { + "type": 0, + "value": "Data correlation and cost models applied" + } + ], + "dataDetailsCloudIntegration": [ + { + "type": 0, + "value": "Cloud integration data" + } + ], + "dataDetailsFinalized": [ + { + "type": 0, + "value": "Finalizing cost" + } + ], + "dataDetailsMetricsOperator": [ + { + "type": 0, + "value": "Cost Management metrics operator data" + } + ], + "dataDetailsProcessed": [ + { + "options": { + "aws": { + "value": [ + { + "type": 0, + "value": "New data processed from Amazon Web Services" + } + ] + }, + "azure": { + "value": [ + { + "type": 0, + "value": "New data processed from Microsoft Azure" + } + ] + }, + "gcp": { + "value": [ + { + "type": 0, + "value": "New data processed from Google Cloud Platform" + } + ] + }, + "ibm": { + "value": [ + { + "type": 0, + "value": "New data processed from IBM Cloud" + } + ] + }, + "oci": { + "value": [ + { + "type": 0, + "value": "New data processed from Oracle Cloud Infrastructure" + } + ] + }, + "ocp": { + "value": [ + { + "type": 0, + "value": "New data processed from OpenShift cluster" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "value" + } + ], + "dataDetailsProcessedAriaLabel": [ + { + "type": 0, + "value": "New data processed" + } + ], + "dataDetailsTransferred": [ + { + "options": { + "aws": { + "value": [ + { + "type": 0, + "value": "New data transferred from Amazon Web Services" } ] }, - "NZD": { + "azure": { "value": [ { "type": 0, - "value": "NZ$" + "value": "New data transferred from Microsoft Azure" } ] }, - "SEK": { + "gcp": { "value": [ { "type": 0, - "value": "SEK" + "value": "New data transferred from Google Cloud Platform" } ] }, - "SGD": { + "ibm": { "value": [ { "type": 0, - "value": "SGD" + "value": "New data transferred from IBM Cloud" } ] }, - "USD": { + "oci": { "value": [ { "type": 0, - "value": "$" + "value": "New data transferred from Oracle Cloud Infrastructure" } ] }, - "ZAR": { + "ocp": { "value": [ { "type": 0, - "value": "ZAR" + "value": "New data transferred from OpenShift cluster" } ] }, @@ -2484,77 +2570,13 @@ } }, "type": 5, - "value": "units" - } - ], - "dashboardCumulativeCostComparison": [ - { - "type": 0, - "value": "Cumulative cost comparison (" - }, - { - "type": 1, - "value": "units" - }, - { - "type": 0, - "value": ")" - } - ], - "dashboardDailyUsageComparison": [ - { - "type": 0, - "value": "Daily usage comparison (" - }, - { - "type": 1, - "value": "units" - }, - { - "type": 0, - "value": ")" - } - ], - "dashboardDatabaseTitle": [ - { - "type": 0, - "value": "Database services cost" - } - ], - "dashboardNetworkTitle": [ - { - "type": 0, - "value": "Network services cost" - } - ], - "dashboardStorageTitle": [ - { - "type": 0, - "value": "Storage services usage" - } - ], - "dashboardTotalCostTooltip": [ - { - "type": 0, - "value": "This total cost is the sum of the infrastructure cost " - }, - { - "type": 1, - "value": "infrastructureCost" - }, - { - "type": 0, - "value": " and supplementary cost " - }, - { - "type": 1, - "value": "supplementaryCost" + "value": "value" } ], - "dataDetails": [ + "dataDetailsTransferredAriaLabel": [ { "type": 0, - "value": "Data details" + "value": "Data transferred" } ], "dataTableAriaLabel": [ @@ -10402,136 +10424,6 @@ "value": "value" } ], - "metricsOperatorData": [ - { - "type": 0, - "value": "Cost Management metrics operator data" - } - ], - "metricsOperatorDataProcessed": [ - { - "options": { - "aws": { - "value": [ - { - "type": 0, - "value": "Data processed from Amazon Web Services" - } - ] - }, - "azure": { - "value": [ - { - "type": 0, - "value": "Data processed from Microsoft Azure" - } - ] - }, - "gcp": { - "value": [ - { - "type": 0, - "value": "Data processed from Google Cloud Platform" - } - ] - }, - "ibm": { - "value": [ - { - "type": 0, - "value": "Data processed from IBM Cloud" - } - ] - }, - "oci": { - "value": [ - { - "type": 0, - "value": "Data processed from Oracle Cloud Infrastructure" - } - ] - }, - "ocp": { - "value": [ - { - "type": 0, - "value": "Data processed from OpenShift" - } - ] - }, - "other": { - "value": [] - } - }, - "type": 5, - "value": "value" - } - ], - "metricsOperatorDataProcessedAriaLabel": [ - { - "type": 0, - "value": "Data received from cloud integration" - } - ], - "metricsOperatorDataReceived": [ - { - "options": { - "aws": { - "value": [ - { - "type": 0, - "value": "Data received from Amazon Web Services" - } - ] - }, - "azure": { - "value": [ - { - "type": 0, - "value": "Data received from Microsoft Azure" - } - ] - }, - "gcp": { - "value": [ - { - "type": 0, - "value": "Data received from Google Cloud Platform" - } - ] - }, - "ibm": { - "value": [ - { - "type": 0, - "value": "Data received from IBM Cloud" - } - ] - }, - "oci": { - "value": [ - { - "type": 0, - "value": "Data received from Oracle Cloud Infrastructure" - } - ] - }, - "ocp": { - "value": [ - { - "type": 0, - "value": "Data received from OpenShift" - } - ] - }, - "other": { - "value": [] - } - }, - "type": 5, - "value": "value" - } - ], "metricsOperatorVersion": [ { "type": 0, @@ -12217,6 +12109,65 @@ "value": "value" } ], + "sourceAvailable": [ + { + "options": { + "aws": { + "value": [ + { + "type": 0, + "value": "Integration available for Amazon Web Services" + } + ] + }, + "azure": { + "value": [ + { + "type": 0, + "value": "Integration available for Microsoft Azure" + } + ] + }, + "gcp": { + "value": [ + { + "type": 0, + "value": "Integration available for Google Cloud Platform" + } + ] + }, + "ibm": { + "value": [ + { + "type": 0, + "value": "Integration available for IBM Cloud" + } + ] + }, + "oci": { + "value": [ + { + "type": 0, + "value": "Integration available for Oracle Cloud Infrastructure" + } + ] + }, + "ocp": { + "value": [ + { + "type": 0, + "value": "Integration available for OpenShift cluster" + } + ] + }, + "other": { + "value": [] + } + }, + "type": 5, + "value": "value" + } + ], "sourceType": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index ce6792ab1..04611fc7c 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -30,8 +30,6 @@ "breakdownTitle": "{value}", "breakdownTotalCostDate": "{value} total cost ({dateRange})", "calculationType": "Calculation type", - "calculations": "Calculating Data and Cost Models", - "calculationsApplied": "Data correlation and cost models applied", "cancel": "Cancel", "chartCostForecastConeLegendLabel": "Cost confidence ({dateRange})", "chartCostForecastConeLegendNoDataLabel": "Cost confidence (no data)", @@ -62,13 +60,6 @@ "chooseValuePlaceholder": "Choose value", "close": "Close", "cloudIntegration": "Cloud integration", - "cloudIntegrationData": "Cloud integration data", - "cloudIntegrationDataChecked": "{value, select, aws {Checked for data from Amazon Web Services} azure {Checked for data from Microsoft Azure} oci {Checked for data from Oracle Cloud Infrastructure} gcp {Checked for data from Google Cloud Platform} ibm {Checked for data from IBM Cloud} other {}}", - "cloudIntegrationDataCheckedAriaLabel": "Checked for data from cloud integration", - "cloudIntegrationDataProcessed": "{value, select, aws {Data processed from Amazon Web Services} azure {Data processed from Microsoft Azure} oci {Data processed from Oracle Cloud Infrastructure} gcp {Data processed from Google Cloud Platform} ibm {Data processed from IBM Cloud} other {}}", - "cloudIntegrationDataProcessedAriaLabel": "Data processed from cloud integration", - "cloudIntegrationDataTransferred": "{value, select, aws {Data transferred from Amazon Web Services} azure {Data transferred from Microsoft Azure} oci {Data transferred from Oracle Cloud Infrastructure} gcp {Data transferred from Google Cloud Platform} ibm {Data transferred from IBM Cloud} other {}}", - "cloudIntegrationDataTransferredAriaLabel": "Data transferred from cloud integration", "cluster": "Cluster", "clusterId": "Cluster id", "clusterInfo": "Cluster information", @@ -205,7 +196,17 @@ "dashboardNetworkTitle": "Network services cost", "dashboardStorageTitle": "Storage services usage", "dashboardTotalCostTooltip": "This total cost is the sum of the infrastructure cost {infrastructureCost} and supplementary cost {supplementaryCost}", + "dataChecked": "{value, select, aws {Checked for data from Amazon Web Services} azure {Checked for data from Microsoft Azure} oci {Checked for data from Oracle Cloud Infrastructure} gcp {Checked for data from Google Cloud Platform} ibm {Checked for data from IBM Cloud} other {}}", + "dataCheckedAriaLabel": "Checked for data from cloud integration", "dataDetails": "Data details", + "dataDetailsCalculated": "Data correlation and cost models applied", + "dataDetailsCloudIntegration": "Cloud integration data", + "dataDetailsFinalized": "Finalizing cost", + "dataDetailsMetricsOperator": "Cost Management metrics operator data", + "dataDetailsProcessed": "{value, select, aws {New data processed from Amazon Web Services} azure {New data processed from Microsoft Azure} oci {New data processed from Oracle Cloud Infrastructure} gcp {New data processed from Google Cloud Platform} ibm {New data processed from IBM Cloud} ocp {New data processed from OpenShift cluster} other {}}", + "dataDetailsProcessedAriaLabel": "New data processed", + "dataDetailsTransferred": "{value, select, aws {New data transferred from Amazon Web Services} azure {New data transferred from Microsoft Azure} oci {New data transferred from Oracle Cloud Infrastructure} gcp {New data transferred from Google Cloud Platform} ibm {New data transferred from IBM Cloud} ocp {New data transferred from OpenShift cluster} other {}}", + "dataDetailsTransferredAriaLabel": "Data transferred", "dataTableAriaLabel": "Details table", "datePickerAfterError": "Date is after the allowable range", "datePickerBeforeError": "Date is before the allowable range", @@ -375,10 +376,6 @@ "metric": "Metric", "metricPlaceholder": "Filter by metrics", "metricValues": "{value, select, cpu {CPU} cluster {Cluster} memory {Memory} node {Node} persistent_volume_claims {Persistent volume claims} storage {Storage} other {}}", - "metricsOperatorData": "Cost Management metrics operator data", - "metricsOperatorDataProcessed": "{value, select, aws {Data processed from Amazon Web Services} azure {Data processed from Microsoft Azure} oci {Data processed from Oracle Cloud Infrastructure} gcp {Data processed from Google Cloud Platform} ibm {Data processed from IBM Cloud} ocp {Data processed from OpenShift} other {}}", - "metricsOperatorDataProcessedAriaLabel": "Data received from cloud integration", - "metricsOperatorDataReceived": "{value, select, aws {Data received from Amazon Web Services} azure {Data received from Microsoft Azure} oci {Data received from Oracle Cloud Infrastructure} gcp {Data received from Google Cloud Platform} ibm {Data received from IBM Cloud} ocp {Data received from OpenShift} other {}}", "metricsOperatorVersion": "Cost Management operator version", "monthOverMonthChange": "Month over month change", "names": "{count, plural, one {Name} other {Names}}", @@ -540,6 +537,7 @@ "settingsTitle": "Cost Management Settings", "sinceDate": "{dateRange}", "source": "{value, select, aws {Amazon Web Services source:} azure {Microsoft Azure source:} oci {Oracle Cloud Infrastructure source:} gcp {Google Cloud Platform source:} ocp {OpenShift source:} other {}}", + "sourceAvailable": "{value, select, aws {Integration available for Amazon Web Services} azure {Integration available for Microsoft Azure} oci {Integration available for Oracle Cloud Infrastructure} gcp {Integration available for Google Cloud Platform} ibm {Integration available for IBM Cloud} ocp {Integration available for OpenShift cluster} other {}}", "sourceType": "Integration", "sourceTypes": "{value, select, aws {Amazon Web Services} azure {Microsoft Azure} oci {Oracle Cloud Infrastructure} gcp {Google Cloud Platform} ibm {IBM Cloud} ocp {OpenShift} other {}}", "sources": "Integrations", diff --git a/src/api/providers.ts b/src/api/providers.ts index 7ebfd273d..249db649c 100644 --- a/src/api/providers.ts +++ b/src/api/providers.ts @@ -40,7 +40,23 @@ export interface ProviderCostModel { uuid: string; } +export interface ProviderInfrastructureState { + end?: string; + start?: string; + state?: string; +} + export interface ProviderInfrastructure { + cloud_provider_state?: { + download?: ProviderInfrastructureState; + processing?: ProviderInfrastructureState; + summary?: ProviderInfrastructureState; + }; + paused?: boolean; + source_status?: { + availability_status?: string; + availability_status_error?: string; + }; type?: string; uuid?: string; } @@ -64,9 +80,9 @@ export interface Provider { previous_month_data?: boolean; source_type?: string; status?: { - download?: string; - processing?: string; - summary?: string; + download?: ProviderInfrastructureState; + processing?: ProviderInfrastructureState; + summary?: ProviderInfrastructureState; }; type?: string; uuid?: string; diff --git a/src/components/inactiveSources/inactiveSources.tsx b/src/components/inactiveSources/inactiveSources.tsx index d68c8fc21..5acacf707 100644 --- a/src/components/inactiveSources/inactiveSources.tsx +++ b/src/components/inactiveSources/inactiveSources.tsx @@ -35,7 +35,7 @@ class InactiveSourcesBase extends React.Component { if (providers && providers.data) { providers.data.map(data => { - if (data.active !== true && data.paused !== true) { + if (data.active === false && data.paused === false) { sources.push(data.name); } }); diff --git a/src/locales/messages.ts b/src/locales/messages.ts index c9fadb216..ff4fe3933 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -200,16 +200,6 @@ export default defineMessages({ description: 'Calculation type', id: 'calculationType', }, - calculations: { - defaultMessage: 'Calculating Data and Cost Models', - description: 'Calculating Data and Cost Models', - id: 'calculations', - }, - calculationsApplied: { - defaultMessage: 'Data correlation and cost models applied', - description: 'Data correlation and cost models applied', - id: 'calculationsApplied', - }, cancel: { defaultMessage: 'Cancel', description: 'Cancel', @@ -360,62 +350,6 @@ export default defineMessages({ description: 'Cloud integration', id: 'cloudIntegration', }, - cloudIntegrationData: { - defaultMessage: 'Cloud integration data', - description: 'Cloud integration data', - id: 'cloudIntegrationData', - }, - cloudIntegrationDataChecked: { - defaultMessage: - '{value, select, ' + - 'aws {Checked for data from Amazon Web Services} ' + - 'azure {Checked for data from Microsoft Azure} ' + - 'oci {Checked for data from Oracle Cloud Infrastructure} ' + - 'gcp {Checked for data from Google Cloud Platform} ' + - 'ibm {Checked for data from IBM Cloud} ' + - 'other {}}', - description: 'Checked for data from cloud integration', - id: 'cloudIntegrationDataChecked', - }, - cloudIntegrationDataCheckedAriaLabel: { - defaultMessage: 'Checked for data from cloud integration', - description: 'Check for data from cloud integration', - id: 'cloudIntegrationDataCheckedAriaLabel', - }, - cloudIntegrationDataProcessed: { - defaultMessage: - '{value, select, ' + - 'aws {Data processed from Amazon Web Services} ' + - 'azure {Data processed from Microsoft Azure} ' + - 'oci {Data processed from Oracle Cloud Infrastructure} ' + - 'gcp {Data processed from Google Cloud Platform} ' + - 'ibm {Data processed from IBM Cloud} ' + - 'other {}}', - description: 'Data processed from cloud integration', - id: 'cloudIntegrationDataProcessed', - }, - cloudIntegrationDataProcessedAriaLabel: { - defaultMessage: 'Data processed from cloud integration', - description: 'Data processed from cloud integration', - id: 'cloudIntegrationDataProcessedAriaLabel', - }, - cloudIntegrationDataTransferred: { - defaultMessage: - '{value, select, ' + - 'aws {Data transferred from Amazon Web Services} ' + - 'azure {Data transferred from Microsoft Azure} ' + - 'oci {Data transferred from Oracle Cloud Infrastructure} ' + - 'gcp {Data transferred from Google Cloud Platform} ' + - 'ibm {Data transferred from IBM Cloud} ' + - 'other {}}', - description: 'Data transferred from cloud integration', - id: 'cloudIntegrationDataTransferred', - }, - cloudIntegrationDataTransferredAriaLabel: { - defaultMessage: 'Data transferred from cloud integration', - description: 'Data transferred from cloud integration', - id: 'cloudIntegrationDataTransferredAriaLabel', - }, cluster: { defaultMessage: 'Cluster', description: 'Cluster', @@ -1096,11 +1030,13 @@ export default defineMessages({ description: 'Currency and calculations', id: 'currencyCalcuationsTitle', }, + currencyDesc: { defaultMessage: 'Select the preferred currency view for your organization', description: 'Select the preferred currency view for your organization', id: 'currencyDesc', }, + // See https://www.localeplanet.com/icu/currency.html currencyOptions: { defaultMessage: @@ -1178,11 +1114,84 @@ export default defineMessages({ description: 'total cost is the sum of the infrastructure cost and supplementary cost', id: 'dashboardTotalCostTooltip', }, + dataChecked: { + defaultMessage: + '{value, select, ' + + 'aws {Checked for data from Amazon Web Services} ' + + 'azure {Checked for data from Microsoft Azure} ' + + 'oci {Checked for data from Oracle Cloud Infrastructure} ' + + 'gcp {Checked for data from Google Cloud Platform} ' + + 'ibm {Checked for data from IBM Cloud} ' + + 'other {}}', + description: 'Checked for data from cloud integration', + id: 'dataChecked', + }, + dataCheckedAriaLabel: { + defaultMessage: 'Checked for data from cloud integration', + description: 'Check for data from cloud integration', + id: 'dataCheckedAriaLabel', + }, dataDetails: { defaultMessage: 'Data details', description: 'Data details', id: 'dataDetails', }, + dataDetailsCalculated: { + defaultMessage: 'Data correlation and cost models applied', + description: 'Data correlation and cost models applied', + id: 'dataDetailsCalculated', + }, + dataDetailsCloudIntegration: { + defaultMessage: 'Cloud integration data', + description: 'Cloud integration data', + id: 'dataDetailsCloudIntegration', + }, + dataDetailsFinalized: { + defaultMessage: 'Finalizing cost', + description: 'Finalizing cost', + id: 'dataDetailsFinalized', + }, + dataDetailsMetricsOperator: { + defaultMessage: 'Cost Management metrics operator data', + description: 'Cost Management metrics operator data', + id: 'dataDetailsMetricsOperator', + }, + dataDetailsProcessed: { + defaultMessage: + '{value, select, ' + + 'aws {New data processed from Amazon Web Services} ' + + 'azure {New data processed from Microsoft Azure} ' + + 'oci {New data processed from Oracle Cloud Infrastructure} ' + + 'gcp {New data processed from Google Cloud Platform} ' + + 'ibm {New data processed from IBM Cloud} ' + + 'ocp {New data processed from OpenShift cluster} ' + + 'other {}}', + description: 'New data processed', + id: 'dataDetailsProcessed', + }, + dataDetailsProcessedAriaLabel: { + defaultMessage: 'New data processed', + description: 'New data processed', + id: 'dataDetailsProcessedAriaLabel', + }, + dataDetailsTransferred: { + defaultMessage: + '{value, select, ' + + 'aws {New data transferred from Amazon Web Services} ' + + 'azure {New data transferred from Microsoft Azure} ' + + 'oci {New data transferred from Oracle Cloud Infrastructure} ' + + 'gcp {New data transferred from Google Cloud Platform} ' + + 'ibm {New data transferred from IBM Cloud} ' + + 'ocp {New data transferred from OpenShift cluster} ' + + 'other {}}', + description: 'New data transferred', + id: 'dataDetailsTransferred', + }, + dataDetailsTransferredAriaLabel: { + defaultMessage: 'Data transferred', + description: 'Data transferred', + id: 'dataDetailsTransferredAriaLabel', + }, dataTableAriaLabel: { defaultMessage: 'Details table', description: 'Details table', @@ -2453,47 +2462,6 @@ export default defineMessages({ description: 'Metric values', id: 'metricValues', }, - metricsOperatorData: { - defaultMessage: 'Cost Management metrics operator data', - description: 'Cost Management metrics operator data', - id: 'metricsOperatorData', - }, - metricsOperatorDataProcessed: { - defaultMessage: - '{value, select, ' + - 'aws {Data processed from Amazon Web Services} ' + - 'azure {Data processed from Microsoft Azure} ' + - 'oci {Data processed from Oracle Cloud Infrastructure} ' + - 'gcp {Data processed from Google Cloud Platform} ' + - 'ibm {Data processed from IBM Cloud} ' + - 'ocp {Data processed from OpenShift} ' + - 'other {}}', - description: 'Data processed from cloud integration', - id: 'metricsOperatorDataProcessed', - }, - metricsOperatorDataProcessedAriaLabel: { - defaultMessage: 'Data processed from cloud integration', - description: 'Data processed from cloud integration', - id: 'metricsOperatorDataProcessedAriaLabel', - }, - metricsOperatorDataReceived: { - defaultMessage: - '{value, select, ' + - 'aws {Data received from Amazon Web Services} ' + - 'azure {Data received from Microsoft Azure} ' + - 'oci {Data received from Oracle Cloud Infrastructure} ' + - 'gcp {Data received from Google Cloud Platform} ' + - 'ibm {Data received from IBM Cloud} ' + - 'ocp {Data received from OpenShift} ' + - 'other {}}', - description: 'Data received from cloud integration', - id: 'metricsOperatorDataReceived', - }, - metricsOperatorDataReceivedAriaLabel: { - defaultMessage: 'Data received from cloud integration', - description: 'Data received from cloud integration', - id: 'metricsOperatorDataProcessedAriaLabel', - }, metricsOperatorVersion: { defaultMessage: 'Cost Management operator version', description: 'Cost Management operator version', @@ -3368,6 +3336,19 @@ export default defineMessages({ description: 'Select from the following {value} integrations:', id: 'source', }, + sourceAvailable: { + defaultMessage: + '{value, select, ' + + 'aws {Integration available for Amazon Web Services} ' + + 'azure {Integration available for Microsoft Azure} ' + + 'oci {Integration available for Oracle Cloud Infrastructure} ' + + 'gcp {Integration available for Google Cloud Platform} ' + + 'ibm {Integration available for IBM Cloud} ' + + 'ocp {Integration available for OpenShift cluster} ' + + 'other {}}', + description: 'Integration available', + id: 'sourceAvailable', + }, sourceType: { defaultMessage: 'Integration', description: 'Integration', diff --git a/src/routes/details/components/breakdown/breakdownHeader.styles.ts b/src/routes/details/components/breakdown/breakdownHeader.styles.ts index 08225ef34..20a98dc0a 100644 --- a/src/routes/details/components/breakdown/breakdownHeader.styles.ts +++ b/src/routes/details/components/breakdown/breakdownHeader.styles.ts @@ -31,6 +31,7 @@ export const styles = { description: { color: global_disabled_color_100.value, fontSize: global_FontSize_xs.value, + paddingLeft: '1px', }, header: { backgroundColor: global_BackgroundColor_100.var, diff --git a/src/routes/details/components/breakdown/breakdownHeader.tsx b/src/routes/details/components/breakdown/breakdownHeader.tsx index 7ef5b32a8..9a1666ed4 100644 --- a/src/routes/details/components/breakdown/breakdownHeader.tsx +++ b/src/routes/details/components/breakdown/breakdownHeader.tsx @@ -159,16 +159,14 @@ class BreakdownHeader extends React.Component {
{intl.formatMessage(messages.breakdownTitle, { value: title })} - {description && ( - <> - <div style={styles.description}> - {description} - {clusterInfoComponent && isClusterInfoToggleEnabled ? clusterInfoComponent : null} - </div> - <div>{dataDetailsComponent && isClusterInfoToggleEnabled ? dataDetailsComponent : null}</div> - </> - )} + {description && ( +
+ {description} + {clusterInfoComponent && isClusterInfoToggleEnabled ? clusterInfoComponent : null} + {dataDetailsComponent && isClusterInfoToggleEnabled ?
{dataDetailsComponent}
: null} +
+ )} {showCostDistribution && (
diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx index 463362262..1c86bda0a 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx @@ -111,7 +111,6 @@ const ClusterInfoContent: React.FC = ({ clusterId }: Cl const useMapToProps = (): ClusterInfoContentStateProps => { const dispatch: ThunkDispatch = useDispatch(); - // PermissionsWraper has already made an API request const providersQueryString = getProvidersQuery(providersQuery); const providers = useSelector((state: RootState) => providersSelectors.selectProviders(state, ProviderType.all, providersQueryString) diff --git a/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx index 17720a975..e75a8ae8c 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx @@ -15,7 +15,7 @@ import { FetchStatus } from 'store/common'; import { providersActions, providersSelectors } from 'store/providers'; import { styles } from './dataDetails.styles'; -import { getIcon, getVariant, lookupKey } from './utils/status'; +import { getCloudAvailability, getProgressStepIcon, getStatus, lookupKey } from './utils/status'; interface CloudIntegrationOwnProps { uuid?: string; @@ -57,16 +57,30 @@ const CloudIntegration: React.FC = ({ uuid }: CloudIntegr return ( <> - {intl.formatMessage(messages.cloudIntegrationData)} + {intl.formatMessage(messages.dataDetailsCloudIntegration)} - + + {intl.formatMessage(messages.sourceAvailable, { + value: lookupKey(provider.source_type), + })} + + - {intl.formatMessage(messages.cloudIntegrationDataChecked, { + {intl.formatMessage(messages.dataChecked, { value: lookupKey(provider.source_type), })}
@@ -83,13 +97,13 @@ const CloudIntegration: React.FC = ({ uuid }: CloudIntegr
- {intl.formatMessage(messages.cloudIntegrationDataTransferred, { + {intl.formatMessage(messages.dataDetailsTransferred, { value: lookupKey(provider.source_type), })}
@@ -106,13 +120,13 @@ const CloudIntegration: React.FC = ({ uuid }: CloudIntegr
- {intl.formatMessage(messages.cloudIntegrationDataProcessed, { + {intl.formatMessage(messages.dataDetailsProcessed, { value: lookupKey(provider.source_type), })}
diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts index d661596bb..aadbdd5ec 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.styles.ts @@ -3,11 +3,13 @@ import global_disabled_color_100 from '@patternfly/react-tokens/dist/js/global_d import global_FontSize_xs from '@patternfly/react-tokens/dist/js/global_FontSize_xs'; import global_spacer_lg from '@patternfly/react-tokens/dist/js/global_spacer_lg'; import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; +import global_spacer_sm from '@patternfly/react-tokens/dist/js/global_spacer_sm'; import type React from 'react'; export const styles = { - dataDetails: { + dataDetailsButton: { fontSize: global_FontSize_xs.value, + paddingLeft: global_spacer_sm.value, }, description: { color: global_disabled_color_100.value, diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx index cedd95bf0..1607022c9 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx @@ -33,7 +33,7 @@ const DataDetails: React.FC = ({ clusterId }: DataDetailsProps <>
-
diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx index f3391d79b..0287bbc2c 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx @@ -18,7 +18,7 @@ import { providersActions, providersQuery, providersSelectors } from 'store/prov import { CloudIntegration } from './cloudIntegration'; import { styles } from './dataDetails.styles'; -import { getProgressIcon, getVariant, lookupKey } from './utils/status'; +import { getClusterAvailability, getProgressStepIcon, getStatus, lookupKey } from './utils/status'; interface DataDetailsContentOwnProps { clusterId?: string; @@ -58,86 +58,112 @@ const DataDetailsContent: React.FC = ({ clusterId }: Da ); } - return ( - <> - {clusterInfo?.infrastructure?.uuid && } - - {intl.formatMessage(messages.metricsOperatorData)} - - - { + return ( + <> + + {intl.formatMessage(messages.dataDetailsMetricsOperator)} + + - {intl.formatMessage(messages.metricsOperatorDataReceived, { - value: lookupKey(clusterInfo.source_type), - })} -
- {intl.formatDate(clusterInfo.last_payload_received_at, { - day: 'numeric', - hour: 'numeric', - hour12: false, - minute: 'numeric', - month: 'short', - timeZone: 'UTC', - timeZoneName: 'short', - year: 'numeric', + + {intl.formatMessage(messages.sourceAvailable, { + value: lookupKey(clusterInfo.source_type), })} -
-
- - {intl.formatMessage(messages.metricsOperatorDataProcessed, { - value: lookupKey(clusterInfo.source_type), - })} -
- {intl.formatDate(clusterInfo.last_payload_received_at, { - day: 'numeric', - hour: 'numeric', - hour12: false, - minute: 'numeric', - month: 'short', - timeZone: 'UTC', - timeZoneName: 'short', - year: 'numeric', + + + {intl.formatMessage(messages.dataDetailsTransferred, { + value: lookupKey(clusterInfo.source_type), })} -
-
-
- - {intl.formatMessage(messages.calculations)} - - - - {intl.formatMessage(messages.calculationsApplied)} -
- {intl.formatDate(clusterInfo.last_payload_received_at, { - day: 'numeric', - hour: 'numeric', - hour12: false, - minute: 'numeric', - month: 'short', - timeZone: 'UTC', - timeZoneName: 'short', - year: 'numeric', +
+ {intl.formatDate(clusterInfo.last_payload_received_at, { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + })} +
+ + + {intl.formatMessage(messages.dataDetailsProcessed, { + value: lookupKey(clusterInfo.source_type), })} -
-
-
+
+ {intl.formatDate(clusterInfo.last_payload_received_at, { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + })} +
+ + + + {intl.formatMessage(messages.dataDetailsFinalized)} + + + + {intl.formatMessage(messages.dataDetailsCalculated)} +
+ {intl.formatDate(clusterInfo.last_payload_received_at, { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + })} +
+
+
+ + ); + }; + + return ( + <> + {clusterInfo?.infrastructure?.uuid && } + {getOperator()} ); }; @@ -146,7 +172,6 @@ const DataDetailsContent: React.FC = ({ clusterId }: Da const useMapToProps = (): DataDetailsContentStateProps => { const dispatch: ThunkDispatch = useDispatch(); - // PermissionsWraper has already made an API request const providersQueryString = getProvidersQuery(providersQuery); const providers = useSelector((state: RootState) => providersSelectors.selectProviders(state, ProviderType.all, providersQueryString) diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx index a03346754..54440b800 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx @@ -2,19 +2,21 @@ import type { Providers } from 'api/providers'; import { ProviderType } from 'api/providers'; import { getProvidersQuery } from 'api/queries/providersQuery'; import type { AxiosError } from 'axios/index'; -import messages from 'locales/messages'; -import React, { useEffect } from 'react'; -import { useIntl } from 'react-intl'; -import { useDispatch, useSelector } from 'react-redux'; -import type { AnyAction } from 'redux'; -import type { ThunkDispatch } from 'redux-thunk'; +import React from 'react'; +import { useSelector } from 'react-redux'; import { filterProviders } from 'routes/utils/providers'; import type { RootState } from 'store'; -import { FetchStatus } from 'store/common'; -import { providersActions, providersQuery, providersSelectors } from 'store/providers'; +import type { FetchStatus } from 'store/common'; +import { providersQuery, providersSelectors } from 'store/providers'; import { styles } from './dataDetails.styles'; -import { getIcon, lookupKey } from './utils/status'; +import { + getCloudAvailability, + getCloudStatus, + getClusterAvailability, + getClusterStatus, + getStatusIcon, +} from './utils/status'; interface DataStatusOwnProps { clusterId?: string; @@ -30,7 +32,6 @@ interface DataStatusStateProps { type DataStatusProps = DataStatusOwnProps; const DataStatus: React.FC = ({ clusterId }: DataStatusProps) => { - const intl = useIntl(); const { providers, providersError } = useMapToProps(); if (!providers || providersError) { @@ -43,21 +44,32 @@ const DataStatus: React.FC = ({ clusterId }: DataStatusProps) = cluster => cluster.authentication?.credentials?.cluster_id === clusterId ); - return ( - <> - {getIcon(clusterInfo?.status?.summary)} - {intl.formatMessage(messages.calculationsApplied, { - value: lookupKey(clusterInfo?.status?.summary), - })} - - ); + const getOverallStatus = () => { + let status; + + const cloudStatus = getCloudStatus(clusterInfo); + const clusterStatus = getClusterStatus(clusterInfo); + + if (getCloudAvailability(clusterInfo) === 'failed' || getClusterAvailability(clusterInfo) === 'failed') { + status = 'failed'; + } else if (cloudStatus === 'failed' || clusterStatus === 'failed') { + status = 'failed'; + } else if (cloudStatus === 'in_progress' || clusterStatus === 'in_progress') { + status = 'in_progress'; + } else if (cloudStatus === 'pending' || clusterStatus === 'pending') { + status = 'pending'; + } else if (cloudStatus === 'complete' || clusterStatus === 'complete') { + status = 'complete'; + } + return status; + }; + + return {getStatusIcon(getOverallStatus())}; }; // eslint-disable-next-line no-empty-pattern const useMapToProps = (): DataStatusStateProps => { - const dispatch: ThunkDispatch = useDispatch(); - - // PermissionsWraper has already made an API request + // PermissionsWrapper has already made an API request const providersQueryString = getProvidersQuery(providersQuery); const providers = useSelector((state: RootState) => providersSelectors.selectProviders(state, ProviderType.all, providersQueryString) @@ -69,12 +81,6 @@ const useMapToProps = (): DataStatusStateProps => { providersSelectors.selectProvidersFetchStatus(state, ProviderType.all, providersQueryString) ); - useEffect(() => { - if (!providersError && providersFetchStatus !== FetchStatus.inProgress) { - dispatch(providersActions.fetchProviders(ProviderType.all, providersQueryString)); - } - }, []); - return { providers, providersError, diff --git a/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx b/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx index 2f74271f8..7d1282d4d 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx @@ -1,46 +1,129 @@ import { Icon } from '@patternfly/react-core'; import { CheckCircleIcon } from '@patternfly/react-icons/dist/esm/icons/check-circle-icon'; -import { CircleIcon } from '@patternfly/react-icons/dist/esm/icons/circle-icon'; +import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon'; import { InProgressIcon } from '@patternfly/react-icons/dist/esm/icons/in-progress-icon'; +import { PendingIcon } from '@patternfly/react-icons/dist/esm/icons/pending-icon'; +import type { Provider } from 'api/providers'; import React from 'react'; export const lookupKey = value => (value ? value.toLowerCase().replace('-', '_') : undefined); -export const getIcon = (status: string) => { - let icon; - let variant; +export const getCloudAvailability = (provider: Provider) => { + let status; + if (!provider) { + return status; + } - switch (lookupKey(status)) { - case 'complete': - icon = ; - variant = 'success'; - break; - case 'in_progress': - icon = ; - break; - case 'pending': - icon = ; - break; - default: - icon = null; - break; + if ( + provider.infrastructure.source_status?.availability_status !== 'available' && + provider.infrastructure.paused === false + ) { + status = 'failed'; // Inactive sources + } else { + status = 'complete'; } - return icon ? {icon} : null; + return status; +}; + +export const getCloudStatus = (provider: Provider) => { + let status; + if (!provider) { + return status; + } + + if ( + provider.status.download?.state === 'failed' || + provider.status.processing?.state === 'failed' || + provider.status.summary?.state === 'failed' + ) { + status = 'failed'; + } else if ( + provider.status.download?.state === 'in_progress' || + provider.status.processing?.state === 'in_progress' || + provider.status.summary?.state === 'in_progress' + ) { + status = 'in_progress'; + } else if ( + provider.status.download?.state === 'pending' || + provider.status.processing?.state === 'pending' || + provider.status.summary?.state === 'pending' + ) { + status = 'pending'; + } else if ( + provider.status.download?.state === 'complete' && + provider.status.processing?.state === 'complete' && + provider.status.summary?.state === 'complete' + ) { + status = 'complete'; + } + return status; +}; + +export const getClusterAvailability = (provider: Provider) => { + let status; + if (!provider) { + return status; + } + + if (provider.active === false && provider.paused === false) { + status = 'failed'; // Inactive sources + } else { + status = 'complete'; + } + return status; }; -export const getProgressIcon = (status: string) => { +export const getClusterStatus = (provider: Provider) => { + let status; + if (!provider) { + return status; + } + + if ( + provider.status.download?.state === 'failed' || + provider.status.processing?.state === 'failed' || + provider.status.summary?.state === 'failed' + ) { + status = 'failed'; + } else if ( + provider.status.download?.state === 'in_progress' || + provider.status.processing?.state === 'in_progress' || + provider.status.summary?.state === 'in_progress' + ) { + status = 'in_progress'; + } else if ( + provider.status.download?.state === 'pending' || + provider.status.processing?.state === 'pending' || + provider.status.summary?.state === 'pending' + ) { + status = 'pending'; + } else if ( + provider.status.download?.state === 'complete' || + provider.status.processing?.state === 'complete' || + provider.status.summary?.state === 'complete' + ) { + status = 'complete'; + } + return status; +}; + +export const getProgressStepIcon = (status: string) => { const key = lookupKey(status); if (key === 'in_progress') { return ; + } else if (key === 'pending') { + return ; } return undefined; }; -export const getVariant = (status: string) => { - const key = lookupKey(status); +export const getStatus = (val: string) => { + const key = lookupKey(val); switch (key) { case 'complete': return 'success'; + case 'failed': + return 'danger'; case 'in_progress': case 'pending': return 'pending'; @@ -48,3 +131,28 @@ export const getVariant = (status: string) => { return 'default'; } }; + +export const getStatusIcon = (status: string) => { + let icon; + let variant; + + switch (lookupKey(status)) { + case 'complete': + icon = ; + variant = 'success'; + break; + case 'failed': + icon = ; + variant = 'danger'; + break; + case 'in_progress': + icon = ; + break; + case 'pending': + icon = ; + break; + default: + break; + } + return icon ? {icon} : null; +}; From bcfdb901e0230f554a3a764f88b49bc4cbc80b5e Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Fri, 15 Mar 2024 14:06:41 -0400 Subject: [PATCH 55/61] Refactored to match latest UX mocks https://issues.redhat.com/browse/COST-4761 --- locales/data.json | 260 ++---------------- locales/translations.json | 19 +- src/locales/messages.ts | 122 +++----- .../clusterInfo/cloudIntegration.tsx | 104 ------- .../clusterInfo/clusterInfoContent.tsx | 51 ++-- .../components/cloudIntegration.tsx | 45 +++ .../dataDetails/cloudIntegration.tsx | 179 ------------ .../dataDetails/components/cloudIData.tsx | 78 ++++++ .../dataDetails/components/clusterData.tsx | 73 +++++ .../dataDetails/components/costData.tsx | 51 ++++ .../dataDetails/components/overallStatus.tsx | 98 +++++++ .../ocpBreakdown/dataDetails/dataDetails.tsx | 4 +- .../dataDetails/dataDetailsContent.tsx | 139 +--------- .../ocpBreakdown/dataDetails/dataStatus.tsx | 92 ------- .../ocpBreakdown/dataDetails/utils/format.ts | 17 ++ .../ocpBreakdown/dataDetails/utils/icon.tsx | 50 ++++ .../ocpBreakdown/dataDetails/utils/status.ts | 85 ++++++ .../ocpBreakdown/dataDetails/utils/status.tsx | 158 ----------- .../ocpBreakdown/dataDetails/utils/variant.ts | 16 ++ 19 files changed, 626 insertions(+), 1015 deletions(-) delete mode 100644 src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx create mode 100644 src/routes/details/ocpBreakdown/clusterInfo/components/cloudIntegration.tsx delete mode 100644 src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/components/cloudIData.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/components/clusterData.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/components/costData.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/components/overallStatus.tsx delete mode 100644 src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/utils/format.ts create mode 100644 src/routes/details/ocpBreakdown/dataDetails/utils/icon.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/utils/status.ts delete mode 100644 src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx create mode 100644 src/routes/details/ocpBreakdown/dataDetails/utils/variant.ts diff --git a/locales/data.json b/locales/data.json index 8d6c3093a..86879488f 100644 --- a/locales/data.json +++ b/locales/data.json @@ -2362,221 +2362,70 @@ "value": "supplementaryCost" } ], - "dataChecked": [ + "dataDetails": [ { - "options": { - "aws": { - "value": [ - { - "type": 0, - "value": "Checked for data from Amazon Web Services" - } - ] - }, - "azure": { - "value": [ - { - "type": 0, - "value": "Checked for data from Microsoft Azure" - } - ] - }, - "gcp": { - "value": [ - { - "type": 0, - "value": "Checked for data from Google Cloud Platform" - } - ] - }, - "ibm": { - "value": [ - { - "type": 0, - "value": "Checked for data from IBM Cloud" - } - ] - }, - "oci": { - "value": [ - { - "type": 0, - "value": "Checked for data from Oracle Cloud Infrastructure" - } - ] - }, - "other": { - "value": [] - } - }, - "type": 5, - "value": "value" + "type": 0, + "value": "Data details" } ], - "dataCheckedAriaLabel": [ + "dataDetailsAvailability": [ { "type": 0, - "value": "Checked for data from cloud integration" + "value": "Data availability" } ], - "dataDetails": [ + "dataDetailsCloudData": [ { "type": 0, - "value": "Data details" + "value": "Cloud data" } ], - "dataDetailsCalculated": [ + "dataDetailsCloudIntegration": [ { "type": 0, - "value": "Data correlation and cost models applied" + "value": "Cloud integration data" } ], - "dataDetailsCloudIntegration": [ + "dataDetailsCloudIntegrationStatus": [ { "type": 0, - "value": "Cloud integration data" + "value": "Cloud integration status" } ], - "dataDetailsFinalized": [ + "dataDetailsClusterData": [ { "type": 0, - "value": "Finalizing cost" + "value": "Cluster data" } ], - "dataDetailsMetricsOperator": [ + "dataDetailsCostManagementData": [ { "type": 0, - "value": "Cost Management metrics operator data" + "value": "Cost Management data" } ], - "dataDetailsProcessed": [ + "dataDetailsIntegrationAndFinalization": [ { - "options": { - "aws": { - "value": [ - { - "type": 0, - "value": "New data processed from Amazon Web Services" - } - ] - }, - "azure": { - "value": [ - { - "type": 0, - "value": "New data processed from Microsoft Azure" - } - ] - }, - "gcp": { - "value": [ - { - "type": 0, - "value": "New data processed from Google Cloud Platform" - } - ] - }, - "ibm": { - "value": [ - { - "type": 0, - "value": "New data processed from IBM Cloud" - } - ] - }, - "oci": { - "value": [ - { - "type": 0, - "value": "New data processed from Oracle Cloud Infrastructure" - } - ] - }, - "ocp": { - "value": [ - { - "type": 0, - "value": "New data processed from OpenShift cluster" - } - ] - }, - "other": { - "value": [] - } - }, - "type": 5, - "value": "value" + "type": 0, + "value": "Data integration and finalization" } ], - "dataDetailsProcessedAriaLabel": [ + "dataDetailsOperatorStatus": [ { "type": 0, - "value": "New data processed" + "value": "Operator status" } ], - "dataDetailsTransferred": [ + "dataDetailsProcessing": [ { - "options": { - "aws": { - "value": [ - { - "type": 0, - "value": "New data transferred from Amazon Web Services" - } - ] - }, - "azure": { - "value": [ - { - "type": 0, - "value": "New data transferred from Microsoft Azure" - } - ] - }, - "gcp": { - "value": [ - { - "type": 0, - "value": "New data transferred from Google Cloud Platform" - } - ] - }, - "ibm": { - "value": [ - { - "type": 0, - "value": "New data transferred from IBM Cloud" - } - ] - }, - "oci": { - "value": [ - { - "type": 0, - "value": "New data transferred from Oracle Cloud Infrastructure" - } - ] - }, - "ocp": { - "value": [ - { - "type": 0, - "value": "New data transferred from OpenShift cluster" - } - ] - }, - "other": { - "value": [] - } - }, - "type": 5, - "value": "value" + "type": 0, + "value": "Data processing" } ], - "dataDetailsTransferredAriaLabel": [ + "dataDetailsRetrieval": [ { "type": 0, - "value": "Data transferred" + "value": "Data retrieval" } ], "dataTableAriaLabel": [ @@ -12109,65 +11958,6 @@ "value": "value" } ], - "sourceAvailable": [ - { - "options": { - "aws": { - "value": [ - { - "type": 0, - "value": "Integration available for Amazon Web Services" - } - ] - }, - "azure": { - "value": [ - { - "type": 0, - "value": "Integration available for Microsoft Azure" - } - ] - }, - "gcp": { - "value": [ - { - "type": 0, - "value": "Integration available for Google Cloud Platform" - } - ] - }, - "ibm": { - "value": [ - { - "type": 0, - "value": "Integration available for IBM Cloud" - } - ] - }, - "oci": { - "value": [ - { - "type": 0, - "value": "Integration available for Oracle Cloud Infrastructure" - } - ] - }, - "ocp": { - "value": [ - { - "type": 0, - "value": "Integration available for OpenShift cluster" - } - ] - }, - "other": { - "value": [] - } - }, - "type": 5, - "value": "value" - } - ], "sourceType": [ { "type": 0, diff --git a/locales/translations.json b/locales/translations.json index 04611fc7c..976bc2b35 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -196,17 +196,17 @@ "dashboardNetworkTitle": "Network services cost", "dashboardStorageTitle": "Storage services usage", "dashboardTotalCostTooltip": "This total cost is the sum of the infrastructure cost {infrastructureCost} and supplementary cost {supplementaryCost}", - "dataChecked": "{value, select, aws {Checked for data from Amazon Web Services} azure {Checked for data from Microsoft Azure} oci {Checked for data from Oracle Cloud Infrastructure} gcp {Checked for data from Google Cloud Platform} ibm {Checked for data from IBM Cloud} other {}}", - "dataCheckedAriaLabel": "Checked for data from cloud integration", "dataDetails": "Data details", - "dataDetailsCalculated": "Data correlation and cost models applied", + "dataDetailsAvailability": "Data availability", + "dataDetailsCloudData": "Cloud data", "dataDetailsCloudIntegration": "Cloud integration data", - "dataDetailsFinalized": "Finalizing cost", - "dataDetailsMetricsOperator": "Cost Management metrics operator data", - "dataDetailsProcessed": "{value, select, aws {New data processed from Amazon Web Services} azure {New data processed from Microsoft Azure} oci {New data processed from Oracle Cloud Infrastructure} gcp {New data processed from Google Cloud Platform} ibm {New data processed from IBM Cloud} ocp {New data processed from OpenShift cluster} other {}}", - "dataDetailsProcessedAriaLabel": "New data processed", - "dataDetailsTransferred": "{value, select, aws {New data transferred from Amazon Web Services} azure {New data transferred from Microsoft Azure} oci {New data transferred from Oracle Cloud Infrastructure} gcp {New data transferred from Google Cloud Platform} ibm {New data transferred from IBM Cloud} ocp {New data transferred from OpenShift cluster} other {}}", - "dataDetailsTransferredAriaLabel": "Data transferred", + "dataDetailsCloudIntegrationStatus": "Cloud integration status", + "dataDetailsClusterData": "Cluster data", + "dataDetailsCostManagementData": "Cost Management data", + "dataDetailsIntegrationAndFinalization": "Data integration and finalization", + "dataDetailsOperatorStatus": "Operator status", + "dataDetailsProcessing": "Data processing", + "dataDetailsRetrieval": "Data retrieval", "dataTableAriaLabel": "Details table", "datePickerAfterError": "Date is after the allowable range", "datePickerBeforeError": "Date is before the allowable range", @@ -537,7 +537,6 @@ "settingsTitle": "Cost Management Settings", "sinceDate": "{dateRange}", "source": "{value, select, aws {Amazon Web Services source:} azure {Microsoft Azure source:} oci {Oracle Cloud Infrastructure source:} gcp {Google Cloud Platform source:} ocp {OpenShift source:} other {}}", - "sourceAvailable": "{value, select, aws {Integration available for Amazon Web Services} azure {Integration available for Microsoft Azure} oci {Integration available for Oracle Cloud Infrastructure} gcp {Integration available for Google Cloud Platform} ibm {Integration available for IBM Cloud} ocp {Integration available for OpenShift cluster} other {}}", "sourceType": "Integration", "sourceTypes": "{value, select, aws {Amazon Web Services} azure {Microsoft Azure} oci {Oracle Cloud Infrastructure} gcp {Google Cloud Platform} ibm {IBM Cloud} ocp {OpenShift} other {}}", "sources": "Integrations", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index ff4fe3933..e0fd8116b 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -1114,83 +1114,60 @@ export default defineMessages({ description: 'total cost is the sum of the infrastructure cost and supplementary cost', id: 'dashboardTotalCostTooltip', }, - dataChecked: { - defaultMessage: - '{value, select, ' + - 'aws {Checked for data from Amazon Web Services} ' + - 'azure {Checked for data from Microsoft Azure} ' + - 'oci {Checked for data from Oracle Cloud Infrastructure} ' + - 'gcp {Checked for data from Google Cloud Platform} ' + - 'ibm {Checked for data from IBM Cloud} ' + - 'other {}}', - description: 'Checked for data from cloud integration', - id: 'dataChecked', - }, - dataCheckedAriaLabel: { - defaultMessage: 'Checked for data from cloud integration', - description: 'Check for data from cloud integration', - id: 'dataCheckedAriaLabel', - }, dataDetails: { defaultMessage: 'Data details', description: 'Data details', id: 'dataDetails', }, - dataDetailsCalculated: { - defaultMessage: 'Data correlation and cost models applied', - description: 'Data correlation and cost models applied', - id: 'dataDetailsCalculated', + dataDetailsAvailability: { + defaultMessage: 'Data availability', + description: 'Data availability', + id: 'dataDetailsAvailability', + }, + dataDetailsCloudData: { + defaultMessage: 'Cloud data', + description: 'Cloud data', + id: 'dataDetailsCloudData', }, dataDetailsCloudIntegration: { defaultMessage: 'Cloud integration data', description: 'Cloud integration data', id: 'dataDetailsCloudIntegration', }, - dataDetailsFinalized: { - defaultMessage: 'Finalizing cost', - description: 'Finalizing cost', - id: 'dataDetailsFinalized', - }, - dataDetailsMetricsOperator: { - defaultMessage: 'Cost Management metrics operator data', - description: 'Cost Management metrics operator data', - id: 'dataDetailsMetricsOperator', - }, - dataDetailsProcessed: { - defaultMessage: - '{value, select, ' + - 'aws {New data processed from Amazon Web Services} ' + - 'azure {New data processed from Microsoft Azure} ' + - 'oci {New data processed from Oracle Cloud Infrastructure} ' + - 'gcp {New data processed from Google Cloud Platform} ' + - 'ibm {New data processed from IBM Cloud} ' + - 'ocp {New data processed from OpenShift cluster} ' + - 'other {}}', - description: 'New data processed', - id: 'dataDetailsProcessed', - }, - dataDetailsProcessedAriaLabel: { - defaultMessage: 'New data processed', - description: 'New data processed', - id: 'dataDetailsProcessedAriaLabel', - }, - dataDetailsTransferred: { - defaultMessage: - '{value, select, ' + - 'aws {New data transferred from Amazon Web Services} ' + - 'azure {New data transferred from Microsoft Azure} ' + - 'oci {New data transferred from Oracle Cloud Infrastructure} ' + - 'gcp {New data transferred from Google Cloud Platform} ' + - 'ibm {New data transferred from IBM Cloud} ' + - 'ocp {New data transferred from OpenShift cluster} ' + - 'other {}}', - description: 'New data transferred', - id: 'dataDetailsTransferred', - }, - dataDetailsTransferredAriaLabel: { - defaultMessage: 'Data transferred', - description: 'Data transferred', - id: 'dataDetailsTransferredAriaLabel', + dataDetailsCloudIntegrationStatus: { + defaultMessage: 'Cloud integration status', + description: 'Cloud integration status', + id: 'dataDetailsCloudIntegrationStatus', + }, + dataDetailsClusterData: { + defaultMessage: 'Cluster data', + description: 'Cluster data', + id: 'dataDetailsClusterData', + }, + dataDetailsCostManagementData: { + defaultMessage: 'Cost Management data', + description: 'Cost Management data', + id: 'dataDetailsCostManagementData', + }, + dataDetailsIntegrationAndFinalization: { + defaultMessage: 'Data integration and finalization', + description: 'Data integration and finalization', + id: 'dataDetailsIntegrationAndFinalization', + }, + dataDetailsOperatorStatus: { + defaultMessage: 'Operator status', + description: 'Operator status', + id: 'dataDetailsOperatorStatus', + }, + dataDetailsProcessing: { + defaultMessage: 'Data processing', + description: 'Data processing', + id: 'dataDetailsProcessing', + }, + dataDetailsRetrieval: { + defaultMessage: 'Data retrieval', + description: 'Data retrieval', + id: 'dataDetailsRetrieval', }, dataTableAriaLabel: { defaultMessage: 'Details table', @@ -3336,19 +3313,6 @@ export default defineMessages({ description: 'Select from the following {value} integrations:', id: 'source', }, - sourceAvailable: { - defaultMessage: - '{value, select, ' + - 'aws {Integration available for Amazon Web Services} ' + - 'azure {Integration available for Microsoft Azure} ' + - 'oci {Integration available for Oracle Cloud Infrastructure} ' + - 'gcp {Integration available for Google Cloud Platform} ' + - 'ibm {Integration available for IBM Cloud} ' + - 'ocp {Integration available for OpenShift cluster} ' + - 'other {}}', - description: 'Integration available', - id: 'sourceAvailable', - }, sourceType: { defaultMessage: 'Integration', description: 'Integration', diff --git a/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx deleted file mode 100644 index c5675b499..000000000 --- a/src/routes/details/ocpBreakdown/clusterInfo/cloudIntegration.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { Text, TextList, TextListItem, TextVariants } from '@patternfly/react-core'; -import type { Provider } from 'api/providers'; -import { ProviderType } from 'api/providers'; -import type { AxiosError } from 'axios'; -import messages from 'locales/messages'; -import React, { useEffect } from 'react'; -import { useIntl } from 'react-intl'; -import { useDispatch, useSelector } from 'react-redux'; -import type { AnyAction } from 'redux'; -import type { ThunkDispatch } from 'redux-thunk'; -import { routes } from 'routes'; -import { NotAvailable } from 'routes/components/page/notAvailable'; -import { LoadingState } from 'routes/components/state/loadingState'; -import type { RootState } from 'store'; -import { FetchStatus } from 'store/common'; -import { providersActions, providersSelectors } from 'store/providers'; -import { formatPath, getReleasePath } from 'utils/paths'; - -import { styles } from './clusterInfo.styles'; - -interface CloudIntegrationOwnProps { - uuid?: string; -} - -interface CloudIntegrationStateProps { - provider: Provider; - providerError: AxiosError; - providerFetchStatus: FetchStatus; - providerQueryString: string; - uuid?: string; -} - -type CloudIntegrationProps = CloudIntegrationOwnProps; - -const CloudIntegration: React.FC = ({ uuid }: CloudIntegrationProps) => { - const intl = useIntl(); - - const { provider, providerError, providerFetchStatus } = useMapToProps({ uuid }); - - const title = intl.formatMessage(messages.optimizations); - - if (providerError) { - return ; - } - - const release = getReleasePath(); - - if (providerFetchStatus === FetchStatus.inProgress) { - return ( -
- -
- ); - } - return ( - <> - {intl.formatMessage(messages.cloudIntegration)} - - - - {intl.formatMessage(messages.source, { value: provider?.source_type?.toLowerCase() })} - - {provider?.name} - - {provider?.cost_models?.length === 0 && ( - - {intl.formatMessage(messages.assignCostModel)} - - )} - - - ); -}; - -// eslint-disable-next-line no-empty-pattern -const useMapToProps = ({ uuid }): CloudIntegrationStateProps => { - const dispatch: ThunkDispatch = useDispatch(); - - const providerQueryString = uuid; - const provider = useSelector( - (state: RootState) => providersSelectors.selectProviders(state, ProviderType.uuid, providerQueryString) as Provider - ); - const providerError = useSelector((state: RootState) => - providersSelectors.selectProvidersError(state, ProviderType.uuid, providerQueryString) - ); - const providerFetchStatus = useSelector((state: RootState) => - providersSelectors.selectProvidersFetchStatus(state, ProviderType.uuid, providerQueryString) - ); - - useEffect(() => { - if (!providerError && providerFetchStatus !== FetchStatus.inProgress) { - dispatch(providersActions.fetchProviders(ProviderType.uuid, providerQueryString)); - } - }, []); - - return { - provider, - providerError, - providerFetchStatus, - providerQueryString, - }; -}; - -export { CloudIntegration }; diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx index 1c86bda0a..53210c310 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx +++ b/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx @@ -7,21 +7,19 @@ import { ProviderType } from 'api/providers'; import { getProvidersQuery } from 'api/queries/providersQuery'; import type { AxiosError } from 'axios'; import messages from 'locales/messages'; -import React, { useEffect } from 'react'; +import React from 'react'; import { useIntl } from 'react-intl'; -import { useDispatch, useSelector } from 'react-redux'; -import type { AnyAction } from 'redux'; -import type { ThunkDispatch } from 'redux-thunk'; +import { useSelector } from 'react-redux'; import { routes } from 'routes'; import { NotAvailable } from 'routes/components/page/notAvailable'; import { LoadingState } from 'routes/components/state/loadingState'; +import { CloudIntegration } from 'routes/details/ocpBreakdown/clusterInfo/components/cloudIntegration'; import { filterProviders } from 'routes/utils/providers'; import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; -import { providersActions, providersQuery, providersSelectors } from 'store/providers'; +import { providersQuery, providersSelectors } from 'store/providers'; import { formatPath, getReleasePath } from 'utils/paths'; -import { CloudIntegration } from './cloudIntegration'; import { styles } from './clusterInfo.styles'; interface ClusterInfoContentOwnProps { @@ -42,20 +40,10 @@ const ClusterInfoContent: React.FC = ({ clusterId }: Cl const { providers, providersError, providersFetchStatus } = useMapToProps(); - const title = intl.formatMessage(messages.optimizations); - if (providersError) { - return ; + return ; } - // Filter OCP providers to skip an extra API request - const ocpProviders = filterProviders(providers, ProviderType.ocp); - const clusterInfo = ocpProviders?.data?.find( - cluster => cluster.authentication?.credentials?.cluster_id === clusterId - ); - - const release = getReleasePath(); - if (providersFetchStatus === FetchStatus.inProgress) { return (
@@ -63,6 +51,14 @@ const ClusterInfoContent: React.FC = ({ clusterId }: Cl
); } + + // Filter OCP providers to skip an extra API request + const ocpProviders = filterProviders(providers, ProviderType.ocp); + const clusterProvider = ocpProviders?.data?.find(val => val.authentication?.credentials?.cluster_id === clusterId); + const cloudProvider = providers?.data?.find(val => val.uuid === clusterProvider?.infrastructure?.uuid); + + const release = getReleasePath(); + return ( {intl.formatMessage(messages.clusterId)} @@ -71,7 +67,7 @@ const ClusterInfoContent: React.FC = ({ clusterId }: Cl {clusterId} {intl.formatMessage(messages.ocpClusterDetails)} - {clusterInfo?.cost_models?.length === 0 && ( + {clusterProvider?.cost_models?.length === 0 && ( {intl.formatMessage(messages.assignCostModel)} @@ -80,8 +76,8 @@ const ClusterInfoContent: React.FC = ({ clusterId }: Cl {intl.formatMessage(messages.metricsOperatorVersion)} - {clusterInfo?.additional_context?.operator_version} - {clusterInfo?.additional_context?.operator_update_available && ( + {clusterProvider?.additional_context?.operator_version} + {clusterProvider?.additional_context?.operator_update_available && ( <> @@ -91,16 +87,16 @@ const ClusterInfoContent: React.FC = ({ clusterId }: Cl )} - {clusterInfo && ( + {clusterProvider && ( <> {intl.formatMessage(messages.redHatIntegration)} {intl.formatMessage(messages.source, { value: 'ocp' })} - {clusterInfo.name} + {clusterProvider.name} - {clusterInfo?.infrastructure?.uuid && } + {cloudProvider && } )} @@ -109,8 +105,7 @@ const ClusterInfoContent: React.FC = ({ clusterId }: Cl // eslint-disable-next-line no-empty-pattern const useMapToProps = (): ClusterInfoContentStateProps => { - const dispatch: ThunkDispatch = useDispatch(); - + // PermissionsWrapper has already made an API request const providersQueryString = getProvidersQuery(providersQuery); const providers = useSelector((state: RootState) => providersSelectors.selectProviders(state, ProviderType.all, providersQueryString) @@ -122,12 +117,6 @@ const useMapToProps = (): ClusterInfoContentStateProps => { providersSelectors.selectProvidersFetchStatus(state, ProviderType.all, providersQueryString) ); - useEffect(() => { - if (!providersError && providersFetchStatus !== FetchStatus.inProgress) { - dispatch(providersActions.fetchProviders(ProviderType.all, providersQueryString)); - } - }, []); - return { providers, providersError, diff --git a/src/routes/details/ocpBreakdown/clusterInfo/components/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/clusterInfo/components/cloudIntegration.tsx new file mode 100644 index 000000000..840384752 --- /dev/null +++ b/src/routes/details/ocpBreakdown/clusterInfo/components/cloudIntegration.tsx @@ -0,0 +1,45 @@ +import { Text, TextList, TextListItem, TextVariants } from '@patternfly/react-core'; +import type { Provider } from 'api/providers'; +import messages from 'locales/messages'; +import React from 'react'; +import { useIntl } from 'react-intl'; +import { routes } from 'routes'; +import { styles } from 'routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles'; +import { formatPath, getReleasePath } from 'utils/paths'; + +interface CloudIntegrationOwnProps { + provider: Provider; +} + +type CloudIntegrationProps = CloudIntegrationOwnProps; + +const CloudIntegration: React.FC = ({ provider }: CloudIntegrationProps) => { + const intl = useIntl(); + + if (!provider) { + return null; + } + + const release = getReleasePath(); + + return ( + <> + {intl.formatMessage(messages.cloudIntegration)} + + + + {intl.formatMessage(messages.source, { value: provider?.source_type?.toLowerCase() })} + + {provider?.name} + + {provider?.cost_models?.length === 0 && ( + + {intl.formatMessage(messages.assignCostModel)} + + )} + + + ); +}; + +export { CloudIntegration }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx deleted file mode 100644 index e75a8ae8c..000000000 --- a/src/routes/details/ocpBreakdown/dataDetails/cloudIntegration.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import { ProgressStep, ProgressStepper, Text, TextContent, TextVariants } from '@patternfly/react-core'; -import type { Provider } from 'api/providers'; -import { ProviderType } from 'api/providers'; -import type { AxiosError } from 'axios'; -import messages from 'locales/messages'; -import React, { useEffect } from 'react'; -import { useIntl } from 'react-intl'; -import { useDispatch, useSelector } from 'react-redux'; -import type { AnyAction } from 'redux'; -import type { ThunkDispatch } from 'redux-thunk'; -import { NotAvailable } from 'routes/components/page/notAvailable'; -import { LoadingState } from 'routes/components/state/loadingState'; -import type { RootState } from 'store'; -import { FetchStatus } from 'store/common'; -import { providersActions, providersSelectors } from 'store/providers'; - -import { styles } from './dataDetails.styles'; -import { getCloudAvailability, getProgressStepIcon, getStatus, lookupKey } from './utils/status'; - -interface CloudIntegrationOwnProps { - uuid?: string; -} - -interface CloudIntegrationStateProps { - provider: Provider; - providerError: AxiosError; - providerFetchStatus: FetchStatus; - providerQueryString: string; - uuid?: string; -} - -type CloudIntegrationProps = CloudIntegrationOwnProps; - -const CloudIntegration: React.FC = ({ uuid }: CloudIntegrationProps) => { - const intl = useIntl(); - - const { provider, providerError, providerFetchStatus } = useMapToProps({ uuid }); - - const title = intl.formatMessage(messages.optimizations); - - if (providerError) { - return ; - } - - if (providerFetchStatus === FetchStatus.inProgress) { - return ( -
- -
- ); - } - - if (!provider) { - return null; - } - - return ( - <> - - {intl.formatMessage(messages.dataDetailsCloudIntegration)} - - - - {intl.formatMessage(messages.sourceAvailable, { - value: lookupKey(provider.source_type), - })} - - - {intl.formatMessage(messages.dataChecked, { - value: lookupKey(provider.source_type), - })} -
- {intl.formatDate(provider.last_payload_received_at, { - day: 'numeric', - hour: 'numeric', - hour12: false, - minute: 'numeric', - month: 'short', - timeZone: 'UTC', - timeZoneName: 'short', - year: 'numeric', - })} -
-
- - {intl.formatMessage(messages.dataDetailsTransferred, { - value: lookupKey(provider.source_type), - })} -
- {intl.formatDate(provider.last_payload_received_at, { - day: 'numeric', - hour: 'numeric', - hour12: false, - minute: 'numeric', - month: 'short', - timeZone: 'UTC', - timeZoneName: 'short', - year: 'numeric', - })} -
-
- - {intl.formatMessage(messages.dataDetailsProcessed, { - value: lookupKey(provider.source_type), - })} -
- {intl.formatDate(provider.last_payload_received_at, { - day: 'numeric', - hour: 'numeric', - hour12: false, - minute: 'numeric', - month: 'short', - timeZone: 'UTC', - timeZoneName: 'short', - year: 'numeric', - })} -
-
-
- - ); -}; - -// eslint-disable-next-line no-empty-pattern -const useMapToProps = ({ uuid }): CloudIntegrationStateProps => { - const dispatch: ThunkDispatch = useDispatch(); - - const providerQueryString = uuid; - const provider = useSelector( - (state: RootState) => providersSelectors.selectProviders(state, ProviderType.uuid, providerQueryString) as Provider - ); - const providerError = useSelector((state: RootState) => - providersSelectors.selectProvidersError(state, ProviderType.uuid, providerQueryString) - ); - const providerFetchStatus = useSelector((state: RootState) => - providersSelectors.selectProvidersFetchStatus(state, ProviderType.uuid, providerQueryString) - ); - - useEffect(() => { - if (!providerError && providerFetchStatus !== FetchStatus.inProgress) { - dispatch(providersActions.fetchProviders(ProviderType.uuid, providerQueryString)); - } - }, []); - - return { - provider, - providerError, - providerFetchStatus, - providerQueryString, - }; -}; - -export { CloudIntegration }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/components/cloudIData.tsx b/src/routes/details/ocpBreakdown/dataDetails/components/cloudIData.tsx new file mode 100644 index 000000000..17de695c1 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/components/cloudIData.tsx @@ -0,0 +1,78 @@ +import { ProgressStep, ProgressStepper, Text, TextContent, TextVariants } from '@patternfly/react-core'; +import type { Provider } from 'api/providers'; +import messages from 'locales/messages'; +import React from 'react'; +import { useIntl } from 'react-intl'; +import { styles } from 'routes/details/ocpBreakdown/dataDetails/dataDetails.styles'; +import { formatDate } from 'routes/details/ocpBreakdown/dataDetails/utils/format'; +import { getProgressStepIcon } from 'routes/details/ocpBreakdown/dataDetails/utils/icon'; +import { getProviderAvailability } from 'routes/details/ocpBreakdown/dataDetails/utils/status'; +import { getProgressStepVariant } from 'routes/details/ocpBreakdown/dataDetails/utils/variant'; + +interface CloudDataOwnProps { + provider: Provider; +} + +type CloudDataProps = CloudDataOwnProps; + +const CloudIData: React.FC = ({ provider }: CloudDataProps) => { + const intl = useIntl(); + + if (!provider) { + return null; + } + + return ( + <> + + {intl.formatMessage(messages.dataDetailsCloudData)} + + + + {intl.formatMessage(messages.dataDetailsCloudIntegrationStatus)} + + + {intl.formatMessage(messages.dataDetailsAvailability)} +
{formatDate(provider.last_payload_received_at)}
+
+ + {intl.formatMessage(messages.dataDetailsRetrieval)} +
+ {formatDate(provider.status.download.end || provider.status.download.start)} +
+
+ + {intl.formatMessage(messages.dataDetailsProcessing)} +
+ {formatDate(provider.status.processing.end || provider.status.processing.start)} +
+
+
+ + ); +}; + +export { CloudIData }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/components/clusterData.tsx b/src/routes/details/ocpBreakdown/dataDetails/components/clusterData.tsx new file mode 100644 index 000000000..2cb95d4fc --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/components/clusterData.tsx @@ -0,0 +1,73 @@ +import { ProgressStep, ProgressStepper, Text, TextContent, TextVariants } from '@patternfly/react-core'; +import type { Provider } from 'api/providers'; +import messages from 'locales/messages'; +import React from 'react'; +import { useIntl } from 'react-intl'; +import { styles } from 'routes/details/ocpBreakdown/dataDetails/dataDetails.styles'; +import { formatDate } from 'routes/details/ocpBreakdown/dataDetails/utils/format'; +import { getProgressStepIcon } from 'routes/details/ocpBreakdown/dataDetails/utils/icon'; +import { getProviderAvailability } from 'routes/details/ocpBreakdown/dataDetails/utils/status'; +import { getProgressStepVariant } from 'routes/details/ocpBreakdown/dataDetails/utils/variant'; + +interface ClusterDataOwnProps { + provider: Provider; +} + +type ClusterDataProps = ClusterDataOwnProps; + +const ClusterData: React.FC = ({ provider }: ClusterDataProps) => { + const intl = useIntl(); + + if (!provider) { + return null; + } + + return ( + <> + + {intl.formatMessage(messages.dataDetailsClusterData)} + + + + {intl.formatMessage(messages.dataDetailsOperatorStatus)} + + + {intl.formatMessage(messages.dataDetailsRetrieval)} +
+ {formatDate(provider.status.download.end || provider.status.download.start)} +
+
+ + {intl.formatMessage(messages.dataDetailsProcessing)} +
+ {formatDate(provider.status.processing.end || provider.status.processing.start)} +
+
+
+ + ); +}; + +export { ClusterData }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/components/costData.tsx b/src/routes/details/ocpBreakdown/dataDetails/components/costData.tsx new file mode 100644 index 000000000..da9b4c373 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/components/costData.tsx @@ -0,0 +1,51 @@ +import { ProgressStep, ProgressStepper, Text, TextContent, TextVariants } from '@patternfly/react-core'; +import type { Provider } from 'api/providers'; +import messages from 'locales/messages'; +import React from 'react'; +import { useIntl } from 'react-intl'; +import { styles } from 'routes/details/ocpBreakdown/dataDetails/dataDetails.styles'; +import { formatDate } from 'routes/details/ocpBreakdown/dataDetails/utils/format'; +import { getProgressStepIcon } from 'routes/details/ocpBreakdown/dataDetails/utils/icon'; +import { getProgressStepVariant } from 'routes/details/ocpBreakdown/dataDetails/utils/variant'; + +interface CostDataOwnProps { + provider: Provider; +} + +type CostDataProps = CostDataOwnProps; + +const CostData: React.FC = ({ provider }: CostDataProps) => { + const intl = useIntl(); + + if (!provider) { + return null; + } + + return ( + <> + + {intl.formatMessage(messages.dataDetailsCostManagementData)} + + + + {intl.formatMessage(messages.dataDetailsIntegrationAndFinalization)} +
+ {formatDate(provider.status.summary.end || provider.status.summary.start)} +
+
+
+ + ); +}; + +export { CostData }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/components/overallStatus.tsx b/src/routes/details/ocpBreakdown/dataDetails/components/overallStatus.tsx new file mode 100644 index 000000000..6a97e1066 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/components/overallStatus.tsx @@ -0,0 +1,98 @@ +import type { Providers } from 'api/providers'; +import { ProviderType } from 'api/providers'; +import { getProvidersQuery } from 'api/queries/providersQuery'; +import type { AxiosError } from 'axios/index'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import { styles } from 'routes/details/ocpBreakdown/dataDetails/dataDetails.styles'; +import { getOverallStatusIcon } from 'routes/details/ocpBreakdown/dataDetails/utils/icon'; +import { + getProviderAvailability, + getProviderStatus, + StatusType, +} from 'routes/details/ocpBreakdown/dataDetails/utils/status'; +import { filterProviders } from 'routes/utils/providers'; +import type { RootState } from 'store'; +import type { FetchStatus } from 'store/common'; +import { providersQuery, providersSelectors } from 'store/providers'; + +interface OverallStatusOwnProps { + clusterId?: string; +} + +interface OverallStatusStateProps { + providers: Providers; + providersError: AxiosError; + providersFetchStatus: FetchStatus; + providersQueryString: string; +} + +type OverallStatusProps = OverallStatusOwnProps; + +const OverallStatus: React.FC = ({ clusterId }: OverallStatusProps) => { + const { providers, providersError } = useMapToProps(); + + if (!providers || providersError) { + return null; + } + + // Filter OCP providers to skip an extra API request + const ocpProviders = filterProviders(providers, ProviderType.ocp); + const clusterProvider = ocpProviders?.data?.find(val => val.authentication?.credentials?.cluster_id === clusterId); + const cloudProvider = providers?.data?.find(val => val.uuid === clusterProvider?.infrastructure?.uuid); + + const getOverallStatus = () => { + let status; + + const cloudAvailability = getProviderAvailability(cloudProvider); + const clusterAvailability = getProviderAvailability(clusterProvider); + const cloudStatus = getProviderStatus(cloudProvider); + const clusterStatus = getProviderStatus(clusterProvider); + + if (cloudAvailability === StatusType.failed || clusterAvailability === StatusType.failed) { + status = StatusType.failed; + } else if (cloudStatus === StatusType.failed || clusterStatus === StatusType.failed) { + status = 'failed'; + } else if (cloudAvailability === StatusType.paused || clusterAvailability === StatusType.paused) { + status = 'paused'; + } else if (cloudStatus === StatusType.inProgress || clusterStatus === StatusType.inProgress) { + status = 'in_progress'; + } else if (cloudStatus === StatusType.pending || clusterStatus === StatusType.pending) { + status = 'pending'; + } else if ( + cloudStatus === StatusType.complete && + clusterStatus === StatusType.complete && + cloudAvailability === StatusType.complete && + clusterAvailability === StatusType.complete + ) { + status = 'complete'; + } + return status; + }; + + return {getOverallStatusIcon(getOverallStatus())}; +}; + +// eslint-disable-next-line no-empty-pattern +const useMapToProps = (): OverallStatusStateProps => { + // PermissionsWrapper has already made an API request + const providersQueryString = getProvidersQuery(providersQuery); + const providers = useSelector((state: RootState) => + providersSelectors.selectProviders(state, ProviderType.all, providersQueryString) + ); + const providersError = useSelector((state: RootState) => + providersSelectors.selectProvidersError(state, ProviderType.all, providersQueryString) + ); + const providersFetchStatus = useSelector((state: RootState) => + providersSelectors.selectProvidersFetchStatus(state, ProviderType.all, providersQueryString) + ); + + return { + providers, + providersError, + providersFetchStatus, + providersQueryString, + }; +}; + +export { OverallStatus }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx index 1607022c9..cd92e79e8 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetails.tsx @@ -3,10 +3,10 @@ import { Modal, ModalBody, ModalHeader, ModalVariant } from '@patternfly/react-c import messages from 'locales/messages'; import React, { useState } from 'react'; import { useIntl } from 'react-intl'; +import { OverallStatus } from 'routes/details/ocpBreakdown/dataDetails/components/overallStatus'; import { styles } from './dataDetails.styles'; import { DataDetailsContent } from './dataDetailsContent'; -import { DataStatus } from './dataStatus'; interface DataDetailsOwnProps { clusterId?: string; @@ -32,7 +32,7 @@ const DataDetails: React.FC = ({ clusterId }: DataDetailsProps return ( <>
- + diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx index 0287bbc2c..0ecafee31 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx +++ b/src/routes/details/ocpBreakdown/dataDetails/dataDetailsContent.tsx @@ -1,24 +1,22 @@ -import { ProgressStep, ProgressStepper, Text, TextContent, TextVariants } from '@patternfly/react-core'; import type { Providers } from 'api/providers'; import { ProviderType } from 'api/providers'; import { getProvidersQuery } from 'api/queries/providersQuery'; import type { AxiosError } from 'axios'; import messages from 'locales/messages'; -import React, { useEffect } from 'react'; +import React from 'react'; import { useIntl } from 'react-intl'; -import { useDispatch, useSelector } from 'react-redux'; -import type { AnyAction } from 'redux'; -import type { ThunkDispatch } from 'redux-thunk'; +import { useSelector } from 'react-redux'; import { NotAvailable } from 'routes/components/page/notAvailable'; import { LoadingState } from 'routes/components/state/loadingState'; +import { CloudIData } from 'routes/details/ocpBreakdown/dataDetails/components/cloudIData'; +import { ClusterData } from 'routes/details/ocpBreakdown/dataDetails/components/clusterData'; +import { CostData } from 'routes/details/ocpBreakdown/dataDetails/components/costData'; import { filterProviders } from 'routes/utils/providers'; import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; -import { providersActions, providersQuery, providersSelectors } from 'store/providers'; +import { providersQuery, providersSelectors } from 'store/providers'; -import { CloudIntegration } from './cloudIntegration'; import { styles } from './dataDetails.styles'; -import { getClusterAvailability, getProgressStepIcon, getStatus, lookupKey } from './utils/status'; interface DataDetailsContentOwnProps { clusterId?: string; @@ -44,12 +42,6 @@ const DataDetailsContent: React.FC = ({ clusterId }: Da return ; } - // Filter OCP providers to skip an extra API request - const ocpProviders = filterProviders(providers, ProviderType.ocp); - const clusterInfo = ocpProviders?.data?.find( - cluster => cluster.authentication?.credentials?.cluster_id === clusterId - ); - if (providersFetchStatus === FetchStatus.inProgress) { return (
@@ -58,120 +50,23 @@ const DataDetailsContent: React.FC = ({ clusterId }: Da ); } - const getOperator = () => { - return ( - <> - - {intl.formatMessage(messages.dataDetailsMetricsOperator)} - - - - {intl.formatMessage(messages.sourceAvailable, { - value: lookupKey(clusterInfo.source_type), - })} - - - {intl.formatMessage(messages.dataDetailsTransferred, { - value: lookupKey(clusterInfo.source_type), - })} -
- {intl.formatDate(clusterInfo.last_payload_received_at, { - day: 'numeric', - hour: 'numeric', - hour12: false, - minute: 'numeric', - month: 'short', - timeZone: 'UTC', - timeZoneName: 'short', - year: 'numeric', - })} -
-
- - {intl.formatMessage(messages.dataDetailsProcessed, { - value: lookupKey(clusterInfo.source_type), - })} -
- {intl.formatDate(clusterInfo.last_payload_received_at, { - day: 'numeric', - hour: 'numeric', - hour12: false, - minute: 'numeric', - month: 'short', - timeZone: 'UTC', - timeZoneName: 'short', - year: 'numeric', - })} -
-
-
- - {intl.formatMessage(messages.dataDetailsFinalized)} - - - - {intl.formatMessage(messages.dataDetailsCalculated)} -
- {intl.formatDate(clusterInfo.last_payload_received_at, { - day: 'numeric', - hour: 'numeric', - hour12: false, - minute: 'numeric', - month: 'short', - timeZone: 'UTC', - timeZoneName: 'short', - year: 'numeric', - })} -
-
-
- - ); - }; + // Filter OCP providers to skip an extra API request + const ocpProviders = filterProviders(providers, ProviderType.ocp); + const clusterProvider = ocpProviders?.data?.find(val => val.authentication?.credentials?.cluster_id === clusterId); + const cloudProvider = providers?.data?.find(val => val.uuid === clusterProvider?.infrastructure?.uuid); return ( <> - {clusterInfo?.infrastructure?.uuid && } - {getOperator()} + {cloudProvider && } + {clusterProvider && } + {clusterProvider && } ); }; // eslint-disable-next-line no-empty-pattern const useMapToProps = (): DataDetailsContentStateProps => { - const dispatch: ThunkDispatch = useDispatch(); - + // PermissionsWrapper has already made an API request const providersQueryString = getProvidersQuery(providersQuery); const providers = useSelector((state: RootState) => providersSelectors.selectProviders(state, ProviderType.all, providersQueryString) @@ -183,12 +78,6 @@ const useMapToProps = (): DataDetailsContentStateProps => { providersSelectors.selectProvidersFetchStatus(state, ProviderType.all, providersQueryString) ); - useEffect(() => { - if (!providersError && providersFetchStatus !== FetchStatus.inProgress) { - dispatch(providersActions.fetchProviders(ProviderType.all, providersQueryString)); - } - }, []); - return { providers, providersError, diff --git a/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx b/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx deleted file mode 100644 index 54440b800..000000000 --- a/src/routes/details/ocpBreakdown/dataDetails/dataStatus.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import type { Providers } from 'api/providers'; -import { ProviderType } from 'api/providers'; -import { getProvidersQuery } from 'api/queries/providersQuery'; -import type { AxiosError } from 'axios/index'; -import React from 'react'; -import { useSelector } from 'react-redux'; -import { filterProviders } from 'routes/utils/providers'; -import type { RootState } from 'store'; -import type { FetchStatus } from 'store/common'; -import { providersQuery, providersSelectors } from 'store/providers'; - -import { styles } from './dataDetails.styles'; -import { - getCloudAvailability, - getCloudStatus, - getClusterAvailability, - getClusterStatus, - getStatusIcon, -} from './utils/status'; - -interface DataStatusOwnProps { - clusterId?: string; -} - -interface DataStatusStateProps { - providers: Providers; - providersError: AxiosError; - providersFetchStatus: FetchStatus; - providersQueryString: string; -} - -type DataStatusProps = DataStatusOwnProps; - -const DataStatus: React.FC = ({ clusterId }: DataStatusProps) => { - const { providers, providersError } = useMapToProps(); - - if (!providers || providersError) { - return null; - } - - // Filter OCP providers to skip an extra API request - const ocpProviders = filterProviders(providers, ProviderType.ocp); - const clusterInfo = ocpProviders?.data?.find( - cluster => cluster.authentication?.credentials?.cluster_id === clusterId - ); - - const getOverallStatus = () => { - let status; - - const cloudStatus = getCloudStatus(clusterInfo); - const clusterStatus = getClusterStatus(clusterInfo); - - if (getCloudAvailability(clusterInfo) === 'failed' || getClusterAvailability(clusterInfo) === 'failed') { - status = 'failed'; - } else if (cloudStatus === 'failed' || clusterStatus === 'failed') { - status = 'failed'; - } else if (cloudStatus === 'in_progress' || clusterStatus === 'in_progress') { - status = 'in_progress'; - } else if (cloudStatus === 'pending' || clusterStatus === 'pending') { - status = 'pending'; - } else if (cloudStatus === 'complete' || clusterStatus === 'complete') { - status = 'complete'; - } - return status; - }; - - return {getStatusIcon(getOverallStatus())}; -}; - -// eslint-disable-next-line no-empty-pattern -const useMapToProps = (): DataStatusStateProps => { - // PermissionsWrapper has already made an API request - const providersQueryString = getProvidersQuery(providersQuery); - const providers = useSelector((state: RootState) => - providersSelectors.selectProviders(state, ProviderType.all, providersQueryString) - ); - const providersError = useSelector((state: RootState) => - providersSelectors.selectProvidersError(state, ProviderType.all, providersQueryString) - ); - const providersFetchStatus = useSelector((state: RootState) => - providersSelectors.selectProvidersFetchStatus(state, ProviderType.all, providersQueryString) - ); - - return { - providers, - providersError, - providersFetchStatus, - providersQueryString, - }; -}; - -export { DataStatus }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/utils/format.ts b/src/routes/details/ocpBreakdown/dataDetails/utils/format.ts new file mode 100644 index 000000000..81ec20a7d --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/utils/format.ts @@ -0,0 +1,17 @@ +import { intl } from 'components/i18n'; + +export const formatDate = (date: string) => { + if (!date) { + return null; + } + return intl.formatDate(date, { + day: 'numeric', + hour: 'numeric', + hour12: false, + minute: 'numeric', + month: 'short', + timeZone: 'UTC', + timeZoneName: 'short', + year: 'numeric', + }); +}; diff --git a/src/routes/details/ocpBreakdown/dataDetails/utils/icon.tsx b/src/routes/details/ocpBreakdown/dataDetails/utils/icon.tsx new file mode 100644 index 000000000..74e85ec1f --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/utils/icon.tsx @@ -0,0 +1,50 @@ +import { Icon } from '@patternfly/react-core'; +import { CheckCircleIcon } from '@patternfly/react-icons/dist/esm/icons/check-circle-icon'; +import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon'; +import { InProgressIcon } from '@patternfly/react-icons/dist/esm/icons/in-progress-icon'; +import { PauseIcon } from '@patternfly/react-icons/dist/esm/icons/pause-icon'; +import { PendingIcon } from '@patternfly/react-icons/dist/esm/icons/pending-icon'; +import React from 'react'; + +import { lookupKey, StatusType } from './status'; + +export const getProgressStepIcon = (value: string) => { + switch (lookupKey(value)) { + case StatusType.inProgress: + return ; + case StatusType.paused: + return ; + case StatusType.pending: + return ; + default: + return undefined; + } +}; + +export const getOverallStatusIcon = (status: StatusType) => { + let icon; + let variant; + + switch (status) { + case StatusType.complete: + icon = ; + variant = 'success'; // Use green color + break; + case StatusType.failed: + icon = ; + variant = 'danger'; // Use red color + break; + case StatusType.inProgress: + icon = ; + break; + case StatusType.paused: + icon = ; + break; + case StatusType.pending: + icon = ; + break; + default: + break; + } + return icon ? {icon} : null; +}; diff --git a/src/routes/details/ocpBreakdown/dataDetails/utils/status.ts b/src/routes/details/ocpBreakdown/dataDetails/utils/status.ts new file mode 100644 index 000000000..2c4112df3 --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/utils/status.ts @@ -0,0 +1,85 @@ +import type { Provider } from 'api/providers'; + +// eslint-disable-next-line no-shadow +export const enum StatusType { + complete = 'complete', + failed = 'failed', + inProgress = 'in_progress', + paused = 'paused', + pending = 'pending', +} + +export const lookupKey = (value: string) => { + switch (normalizeValue(value)) { + case 'complete': + return StatusType.complete; + case 'failed': + return StatusType.failed; + case 'in_progress': + return StatusType.inProgress; + case 'paused': + return StatusType.paused; + case 'pending': + return StatusType.pending; + default: + return undefined; + } +}; + +export const normalizeValue = (value: string) => { + return value ? value.toLowerCase().replace('-', '_') : undefined; +}; + +export const getProviderAvailability = (provider: Provider) => { + let status; + if (!provider) { + return status; + } + + if (provider.active === false && provider.paused === false) { + status = StatusType.failed; // Inactive sources + } else if (provider.infrastructure.paused === true) { + status = StatusType.paused; + } else { + status = StatusType.complete; + } + return status; +}; + +export const getProviderStatus = (provider: Provider) => { + let status; + if (!provider) { + return status; + } + + const downloadState = lookupKey(provider.status.download.state); + const processingState = lookupKey(provider.status.processing.state); + const summaryState = lookupKey(provider.status.summary.state); + + if ( + downloadState === StatusType.failed || + processingState === StatusType.failed || + summaryState === StatusType.failed + ) { + status = StatusType.failed; + } else if ( + downloadState === StatusType.inProgress || + processingState === StatusType.inProgress || + summaryState === StatusType.inProgress + ) { + status = StatusType.inProgress; + } else if ( + downloadState === StatusType.pending || + processingState === StatusType.pending || + summaryState === StatusType.pending + ) { + status = StatusType.pending; + } else if ( + downloadState === StatusType.complete || + processingState === StatusType.complete || + summaryState === StatusType.complete + ) { + status = StatusType.complete; + } + return status; +}; diff --git a/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx b/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx deleted file mode 100644 index 7d1282d4d..000000000 --- a/src/routes/details/ocpBreakdown/dataDetails/utils/status.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import { Icon } from '@patternfly/react-core'; -import { CheckCircleIcon } from '@patternfly/react-icons/dist/esm/icons/check-circle-icon'; -import { ExclamationCircleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon'; -import { InProgressIcon } from '@patternfly/react-icons/dist/esm/icons/in-progress-icon'; -import { PendingIcon } from '@patternfly/react-icons/dist/esm/icons/pending-icon'; -import type { Provider } from 'api/providers'; -import React from 'react'; - -export const lookupKey = value => (value ? value.toLowerCase().replace('-', '_') : undefined); - -export const getCloudAvailability = (provider: Provider) => { - let status; - if (!provider) { - return status; - } - - if ( - provider.infrastructure.source_status?.availability_status !== 'available' && - provider.infrastructure.paused === false - ) { - status = 'failed'; // Inactive sources - } else { - status = 'complete'; - } - return status; -}; - -export const getCloudStatus = (provider: Provider) => { - let status; - if (!provider) { - return status; - } - - if ( - provider.status.download?.state === 'failed' || - provider.status.processing?.state === 'failed' || - provider.status.summary?.state === 'failed' - ) { - status = 'failed'; - } else if ( - provider.status.download?.state === 'in_progress' || - provider.status.processing?.state === 'in_progress' || - provider.status.summary?.state === 'in_progress' - ) { - status = 'in_progress'; - } else if ( - provider.status.download?.state === 'pending' || - provider.status.processing?.state === 'pending' || - provider.status.summary?.state === 'pending' - ) { - status = 'pending'; - } else if ( - provider.status.download?.state === 'complete' && - provider.status.processing?.state === 'complete' && - provider.status.summary?.state === 'complete' - ) { - status = 'complete'; - } - return status; -}; - -export const getClusterAvailability = (provider: Provider) => { - let status; - if (!provider) { - return status; - } - - if (provider.active === false && provider.paused === false) { - status = 'failed'; // Inactive sources - } else { - status = 'complete'; - } - return status; -}; - -export const getClusterStatus = (provider: Provider) => { - let status; - if (!provider) { - return status; - } - - if ( - provider.status.download?.state === 'failed' || - provider.status.processing?.state === 'failed' || - provider.status.summary?.state === 'failed' - ) { - status = 'failed'; - } else if ( - provider.status.download?.state === 'in_progress' || - provider.status.processing?.state === 'in_progress' || - provider.status.summary?.state === 'in_progress' - ) { - status = 'in_progress'; - } else if ( - provider.status.download?.state === 'pending' || - provider.status.processing?.state === 'pending' || - provider.status.summary?.state === 'pending' - ) { - status = 'pending'; - } else if ( - provider.status.download?.state === 'complete' || - provider.status.processing?.state === 'complete' || - provider.status.summary?.state === 'complete' - ) { - status = 'complete'; - } - return status; -}; - -export const getProgressStepIcon = (status: string) => { - const key = lookupKey(status); - if (key === 'in_progress') { - return ; - } else if (key === 'pending') { - return ; - } - return undefined; -}; - -export const getStatus = (val: string) => { - const key = lookupKey(val); - switch (key) { - case 'complete': - return 'success'; - case 'failed': - return 'danger'; - case 'in_progress': - case 'pending': - return 'pending'; - default: - return 'default'; - } -}; - -export const getStatusIcon = (status: string) => { - let icon; - let variant; - - switch (lookupKey(status)) { - case 'complete': - icon = ; - variant = 'success'; - break; - case 'failed': - icon = ; - variant = 'danger'; - break; - case 'in_progress': - icon = ; - break; - case 'pending': - icon = ; - break; - default: - break; - } - return icon ? {icon} : null; -}; diff --git a/src/routes/details/ocpBreakdown/dataDetails/utils/variant.ts b/src/routes/details/ocpBreakdown/dataDetails/utils/variant.ts new file mode 100644 index 000000000..c1d881cfe --- /dev/null +++ b/src/routes/details/ocpBreakdown/dataDetails/utils/variant.ts @@ -0,0 +1,16 @@ +import { lookupKey, StatusType } from './status'; + +export const getProgressStepVariant = (value: string) => { + switch (lookupKey(value)) { + case StatusType.complete: + return 'success'; + case StatusType.failed: + return 'danger'; + case StatusType.pending: + return 'pending'; + case StatusType.inProgress: // Use 'default' status with custom icon + case StatusType.paused: + default: + return 'default'; + } +}; From 39e5e86e62cd24a7b6974f9bb32bd6e878686fa8 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Fri, 15 Mar 2024 16:20:54 -0400 Subject: [PATCH 56/61] Included link to source providers https://issues.redhat.com/browse/COST-4761 --- locales/data.json | 4 +- locales/translations.json | 2 +- src/locales/messages.ts | 8 ++-- .../details/ocpBreakdown/ocpBreakdown.tsx | 4 +- .../clusterInfo/clusterInfo.styles.ts | 0 .../clusterInfo/clusterInfo.tsx | 0 .../clusterInfo/clusterInfoContent.scss | 0 .../clusterInfo/clusterInfoContent.tsx | 2 +- .../components/cloudIntegration.tsx | 11 ++---- .../clusterInfo/index.ts | 0 .../components/sourceLink.styles.ts | 8 ++++ .../providerDetails/components/sourceLink.tsx | 38 +++++++++++++++++++ .../dataDetails/components/cloudIData.tsx | 14 ++++--- .../dataDetails/components/clusterData.tsx | 18 +++++---- .../dataDetails/components/costData.tsx | 8 ++-- .../dataDetails/components/overallStatus.tsx | 6 +-- .../dataDetails/dataDetails.styles.ts | 3 ++ .../dataDetails/dataDetails.tsx | 2 +- .../dataDetails/dataDetailsContent.tsx | 6 +-- .../dataDetails/index.ts | 0 .../dataDetails/utils/format.ts | 0 .../dataDetails/utils/icon.tsx | 0 .../dataDetails/utils/status.ts | 7 +--- .../dataDetails/utils/variant.ts | 0 .../providerDetails/utils/normailize.ts | 3 ++ 25 files changed, 98 insertions(+), 46 deletions(-) rename src/routes/details/ocpBreakdown/{ => providerDetails}/clusterInfo/clusterInfo.styles.ts (100%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/clusterInfo/clusterInfo.tsx (100%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/clusterInfo/clusterInfoContent.scss (100%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/clusterInfo/clusterInfoContent.tsx (97%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/clusterInfo/components/cloudIntegration.tsx (69%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/clusterInfo/index.ts (100%) create mode 100644 src/routes/details/ocpBreakdown/providerDetails/components/sourceLink.styles.ts create mode 100644 src/routes/details/ocpBreakdown/providerDetails/components/sourceLink.tsx rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/components/cloudIData.tsx (84%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/components/clusterData.tsx (77%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/components/costData.tsx (84%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/components/overallStatus.tsx (94%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/dataDetails.styles.ts (94%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/dataDetails.tsx (93%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/dataDetailsContent.tsx (90%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/index.ts (100%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/utils/format.ts (100%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/utils/icon.tsx (100%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/utils/status.ts (92%) rename src/routes/details/ocpBreakdown/{ => providerDetails}/dataDetails/utils/variant.ts (100%) create mode 100644 src/routes/details/ocpBreakdown/providerDetails/utils/normailize.ts diff --git a/locales/data.json b/locales/data.json index 86879488f..74600a0ac 100644 --- a/locales/data.json +++ b/locales/data.json @@ -2410,10 +2410,10 @@ "value": "Data integration and finalization" } ], - "dataDetailsOperatorStatus": [ + "dataDetailsIntegrationStatus": [ { "type": 0, - "value": "Operator status" + "value": "Red Hat integration status" } ], "dataDetailsProcessing": [ diff --git a/locales/translations.json b/locales/translations.json index 976bc2b35..c1e813d7a 100644 --- a/locales/translations.json +++ b/locales/translations.json @@ -204,7 +204,7 @@ "dataDetailsClusterData": "Cluster data", "dataDetailsCostManagementData": "Cost Management data", "dataDetailsIntegrationAndFinalization": "Data integration and finalization", - "dataDetailsOperatorStatus": "Operator status", + "dataDetailsIntegrationStatus": "Red Hat integration status", "dataDetailsProcessing": "Data processing", "dataDetailsRetrieval": "Data retrieval", "dataTableAriaLabel": "Details table", diff --git a/src/locales/messages.ts b/src/locales/messages.ts index e0fd8116b..a36591085 100644 --- a/src/locales/messages.ts +++ b/src/locales/messages.ts @@ -1154,10 +1154,10 @@ export default defineMessages({ description: 'Data integration and finalization', id: 'dataDetailsIntegrationAndFinalization', }, - dataDetailsOperatorStatus: { - defaultMessage: 'Operator status', - description: 'Operator status', - id: 'dataDetailsOperatorStatus', + dataDetailsIntegrationStatus: { + defaultMessage: 'Red Hat integration status', + description: 'Red Hat integration status', + id: 'dataDetailsIntegrationStatus', }, dataDetailsProcessing: { defaultMessage: 'Data processing', diff --git a/src/routes/details/ocpBreakdown/ocpBreakdown.tsx b/src/routes/details/ocpBreakdown/ocpBreakdown.tsx index f967485fd..3e5107d14 100644 --- a/src/routes/details/ocpBreakdown/ocpBreakdown.tsx +++ b/src/routes/details/ocpBreakdown/ocpBreakdown.tsx @@ -12,6 +12,8 @@ import { connect } from 'react-redux'; import { routes } from 'routes'; import type { BreakdownStateProps } from 'routes/details/components/breakdown'; import { BreakdownBase } from 'routes/details/components/breakdown'; +import { ClusterInfo } from 'routes/details/ocpBreakdown/providerDetails/clusterInfo'; +import { DataDetails } from 'routes/details/ocpBreakdown/providerDetails/dataDetails'; import { getGroupById, getGroupByValue } from 'routes/utils/groupBy'; import { filterProviders } from 'routes/utils/providers'; import { getQueryState } from 'routes/utils/queryState'; @@ -26,9 +28,7 @@ import type { RouterComponentProps } from 'utils/router'; import { withRouter } from 'utils/router'; import { getCostDistribution, getCurrency } from 'utils/sessionStorage'; -import { ClusterInfo } from './clusterInfo'; import { CostOverview } from './costOverview'; -import { DataDetails } from './dataDetails'; import { HistoricalData } from './historicalData'; import { OcpBreakdownOptimizations } from './ocpBreakdownOptimizations'; diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts b/src/routes/details/ocpBreakdown/providerDetails/clusterInfo/clusterInfo.styles.ts similarity index 100% rename from src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles.ts rename to src/routes/details/ocpBreakdown/providerDetails/clusterInfo/clusterInfo.styles.ts diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx b/src/routes/details/ocpBreakdown/providerDetails/clusterInfo/clusterInfo.tsx similarity index 100% rename from src/routes/details/ocpBreakdown/clusterInfo/clusterInfo.tsx rename to src/routes/details/ocpBreakdown/providerDetails/clusterInfo/clusterInfo.tsx diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.scss b/src/routes/details/ocpBreakdown/providerDetails/clusterInfo/clusterInfoContent.scss similarity index 100% rename from src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.scss rename to src/routes/details/ocpBreakdown/providerDetails/clusterInfo/clusterInfoContent.scss diff --git a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx b/src/routes/details/ocpBreakdown/providerDetails/clusterInfo/clusterInfoContent.tsx similarity index 97% rename from src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx rename to src/routes/details/ocpBreakdown/providerDetails/clusterInfo/clusterInfoContent.tsx index 53210c310..b6bd7ce84 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/clusterInfoContent.tsx +++ b/src/routes/details/ocpBreakdown/providerDetails/clusterInfo/clusterInfoContent.tsx @@ -13,7 +13,7 @@ import { useSelector } from 'react-redux'; import { routes } from 'routes'; import { NotAvailable } from 'routes/components/page/notAvailable'; import { LoadingState } from 'routes/components/state/loadingState'; -import { CloudIntegration } from 'routes/details/ocpBreakdown/clusterInfo/components/cloudIntegration'; +import { CloudIntegration } from 'routes/details/ocpBreakdown/providerDetails/clusterInfo/components/cloudIntegration'; import { filterProviders } from 'routes/utils/providers'; import type { RootState } from 'store'; import { FetchStatus } from 'store/common'; diff --git a/src/routes/details/ocpBreakdown/clusterInfo/components/cloudIntegration.tsx b/src/routes/details/ocpBreakdown/providerDetails/clusterInfo/components/cloudIntegration.tsx similarity index 69% rename from src/routes/details/ocpBreakdown/clusterInfo/components/cloudIntegration.tsx rename to src/routes/details/ocpBreakdown/providerDetails/clusterInfo/components/cloudIntegration.tsx index 840384752..630f5646e 100644 --- a/src/routes/details/ocpBreakdown/clusterInfo/components/cloudIntegration.tsx +++ b/src/routes/details/ocpBreakdown/providerDetails/clusterInfo/components/cloudIntegration.tsx @@ -4,8 +4,8 @@ import messages from 'locales/messages'; import React from 'react'; import { useIntl } from 'react-intl'; import { routes } from 'routes'; -import { styles } from 'routes/details/ocpBreakdown/clusterInfo/clusterInfo.styles'; -import { formatPath, getReleasePath } from 'utils/paths'; +import { SourceLink } from 'routes/details/ocpBreakdown/providerDetails/components/sourceLink'; +import { formatPath } from 'utils/paths'; interface CloudIntegrationOwnProps { provider: Provider; @@ -20,17 +20,12 @@ const CloudIntegration: React.FC = ({ provider }: CloudIn return null; } - const release = getReleasePath(); - return ( <> {intl.formatMessage(messages.cloudIntegration)} - - {intl.formatMessage(messages.source, { value: provider?.source_type?.toLowerCase() })} - - {provider?.name} + {provider?.cost_models?.length === 0 && ( diff --git a/src/routes/details/ocpBreakdown/clusterInfo/index.ts b/src/routes/details/ocpBreakdown/providerDetails/clusterInfo/index.ts similarity index 100% rename from src/routes/details/ocpBreakdown/clusterInfo/index.ts rename to src/routes/details/ocpBreakdown/providerDetails/clusterInfo/index.ts diff --git a/src/routes/details/ocpBreakdown/providerDetails/components/sourceLink.styles.ts b/src/routes/details/ocpBreakdown/providerDetails/components/sourceLink.styles.ts new file mode 100644 index 000000000..81c67d5f0 --- /dev/null +++ b/src/routes/details/ocpBreakdown/providerDetails/components/sourceLink.styles.ts @@ -0,0 +1,8 @@ +import global_spacer_md from '@patternfly/react-tokens/dist/js/global_spacer_md'; +import type React from 'react'; + +export const styles = { + spacingRight: { + marginRight: global_spacer_md.value, + }, +} as { [className: string]: React.CSSProperties }; diff --git a/src/routes/details/ocpBreakdown/providerDetails/components/sourceLink.tsx b/src/routes/details/ocpBreakdown/providerDetails/components/sourceLink.tsx new file mode 100644 index 000000000..66d4ea5a0 --- /dev/null +++ b/src/routes/details/ocpBreakdown/providerDetails/components/sourceLink.tsx @@ -0,0 +1,38 @@ +import type { Provider } from 'api/providers'; +import messages from 'locales/messages'; +import React from 'react'; +import { useIntl } from 'react-intl'; +import { normalize } from 'routes/details/ocpBreakdown/providerDetails/utils/normailize'; +import { getReleasePath } from 'utils/paths'; + +import { styles } from './sourceLink.styles'; + +interface SourceLinkOwnProps { + provider: Provider; + showLabel?: boolean; +} + +type SourceLinkProps = SourceLinkOwnProps; + +const SourceLink: React.FC = ({ provider, showLabel = true }: SourceLinkProps) => { + const intl = useIntl(); + + if (!provider) { + return null; + } + + const release = getReleasePath(); + + return ( + <> + {showLabel && ( + + {intl.formatMessage(messages.source, { value: normalize(provider?.source_type) })} + + )} + {provider?.name || provider?.uuid} + + ); +}; + +export { SourceLink }; diff --git a/src/routes/details/ocpBreakdown/dataDetails/components/cloudIData.tsx b/src/routes/details/ocpBreakdown/providerDetails/dataDetails/components/cloudIData.tsx similarity index 84% rename from src/routes/details/ocpBreakdown/dataDetails/components/cloudIData.tsx rename to src/routes/details/ocpBreakdown/providerDetails/dataDetails/components/cloudIData.tsx index 17de695c1..d3ed48b1c 100644 --- a/src/routes/details/ocpBreakdown/dataDetails/components/cloudIData.tsx +++ b/src/routes/details/ocpBreakdown/providerDetails/dataDetails/components/cloudIData.tsx @@ -3,11 +3,12 @@ import type { Provider } from 'api/providers'; import messages from 'locales/messages'; import React from 'react'; import { useIntl } from 'react-intl'; -import { styles } from 'routes/details/ocpBreakdown/dataDetails/dataDetails.styles'; -import { formatDate } from 'routes/details/ocpBreakdown/dataDetails/utils/format'; -import { getProgressStepIcon } from 'routes/details/ocpBreakdown/dataDetails/utils/icon'; -import { getProviderAvailability } from 'routes/details/ocpBreakdown/dataDetails/utils/status'; -import { getProgressStepVariant } from 'routes/details/ocpBreakdown/dataDetails/utils/variant'; +import { SourceLink } from 'routes/details/ocpBreakdown/providerDetails/components/sourceLink'; +import { styles } from 'routes/details/ocpBreakdown/providerDetails/dataDetails/dataDetails.styles'; +import { formatDate } from 'routes/details/ocpBreakdown/providerDetails/dataDetails/utils/format'; +import { getProgressStepIcon } from 'routes/details/ocpBreakdown/providerDetails/dataDetails/utils/icon'; +import { getProviderAvailability } from 'routes/details/ocpBreakdown/providerDetails/dataDetails/utils/status'; +import { getProgressStepVariant } from 'routes/details/ocpBreakdown/providerDetails/dataDetails/utils/variant'; interface CloudDataOwnProps { provider: Provider; @@ -36,6 +37,9 @@ const CloudIData: React.FC = ({ provider }: CloudDataProps) => { variant={getProgressStepVariant(getProviderAvailability(provider))} > {intl.formatMessage(messages.dataDetailsCloudIntegrationStatus)} +
+ +
= ({ provider }: ClusterDataProps) style={styles.stepper} > - {intl.formatMessage(messages.dataDetailsOperatorStatus)} + {intl.formatMessage(messages.dataDetailsIntegrationStatus)} +
+ +
{ - switch (normalizeValue(value)) { + switch (normalize(value)) { case 'complete': return StatusType.complete; case 'failed': @@ -26,10 +27,6 @@ export const lookupKey = (value: string) => { } }; -export const normalizeValue = (value: string) => { - return value ? value.toLowerCase().replace('-', '_') : undefined; -}; - export const getProviderAvailability = (provider: Provider) => { let status; if (!provider) { diff --git a/src/routes/details/ocpBreakdown/dataDetails/utils/variant.ts b/src/routes/details/ocpBreakdown/providerDetails/dataDetails/utils/variant.ts similarity index 100% rename from src/routes/details/ocpBreakdown/dataDetails/utils/variant.ts rename to src/routes/details/ocpBreakdown/providerDetails/dataDetails/utils/variant.ts diff --git a/src/routes/details/ocpBreakdown/providerDetails/utils/normailize.ts b/src/routes/details/ocpBreakdown/providerDetails/utils/normailize.ts new file mode 100644 index 000000000..232421e0e --- /dev/null +++ b/src/routes/details/ocpBreakdown/providerDetails/utils/normailize.ts @@ -0,0 +1,3 @@ +export const normalize = (value: string) => { + return value ? value.toLowerCase().replace('-', '_') : undefined; +}; From 15b5a506742309907834018c8625ad62be6c237e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:02:12 +0000 Subject: [PATCH 57/61] (chore): Bump the lint-dependencies group with 2 updates Bumps the lint-dependencies group with 2 updates: [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react). Updates `@typescript-eslint/parser` from 7.1.1 to 7.2.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.2.0/packages/parser) Updates `eslint-plugin-react` from 7.34.0 to 7.34.1 - [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases) - [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/v7.34.1/CHANGELOG.md) - [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.34.0...v7.34.1) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: lint-dependencies - dependency-name: eslint-plugin-react dependency-type: direct:development update-type: version-update:semver-patch dependency-group: lint-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 58 +++++++++++++++++++++++------------------------ package.json | 4 ++-- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index f22765e19..121f3a9a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,7 +59,7 @@ "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.1.1", - "@typescript-eslint/parser": "^7.1.1", + "@typescript-eslint/parser": "^7.2.0", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", "eslint": "^8.57.0", @@ -69,7 +69,7 @@ "eslint-plugin-markdown": "^4.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-react": "^7.34.0", + "eslint-plugin-react": "^7.34.1", "eslint-plugin-simple-import-sort": "^12.0.0", "eslint-plugin-sort-keys-fix": "^1.1.2", "eslint-plugin-testing-library": "^6.2.0", @@ -4486,15 +4486,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.1.tgz", - "integrity": "sha512-ZWUFyL0z04R1nAEgr9e79YtV5LbafdOtN7yapNbn1ansMyaegl2D4bL7vHoJ4HPSc4CaLwuCVas8CVuneKzplQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.1.1", - "@typescript-eslint/types": "7.1.1", - "@typescript-eslint/typescript-estree": "7.1.1", - "@typescript-eslint/visitor-keys": "7.1.1", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4" }, "engines": { @@ -4514,13 +4514,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz", - "integrity": "sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.1", - "@typescript-eslint/visitor-keys": "7.1.1" + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4531,9 +4531,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.1.tgz", - "integrity": "sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4544,13 +4544,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz", - "integrity": "sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.1", - "@typescript-eslint/visitor-keys": "7.1.1", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -4572,12 +4572,12 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz", - "integrity": "sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.1", + "@typescript-eslint/types": "7.2.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -9839,9 +9839,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.0.tgz", - "integrity": "sha512-MeVXdReleBTdkz/bvcQMSnCXGi+c9kvy51IpinjnJgutl3YTHWsDdke7Z1ufZpGfDG8xduBDKyjtB9JH1eBKIQ==", + "version": "7.34.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", + "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", "dev": true, "dependencies": { "array-includes": "^3.1.7", diff --git a/package.json b/package.json index 7dd5f60fd..334858ac0 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.1.1", - "@typescript-eslint/parser": "^7.1.1", + "@typescript-eslint/parser": "^7.2.0", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", "eslint": "^8.57.0", @@ -109,7 +109,7 @@ "eslint-plugin-markdown": "^4.0.1", "eslint-plugin-patternfly-react": "^5.2.1", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-react": "^7.34.0", + "eslint-plugin-react": "^7.34.1", "eslint-plugin-simple-import-sort": "^12.0.0", "eslint-plugin-sort-keys-fix": "^1.1.2", "eslint-plugin-testing-library": "^6.2.0", From 6610e478cdb12caf7843eb0c84520d7a74b54ca2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:12:34 +0000 Subject: [PATCH 58/61] (chore): Bump the ci-dependencies group with 6 updates Bumps the ci-dependencies group with 6 updates: | Package | From | To | | --- | --- | --- | | [@redhat-cloud-services/frontend-components](https://github.com/RedHatInsights/frontend-components) | `4.2.4` | `4.2.5` | | [axios](https://github.com/axios/axios) | `1.6.7` | `1.6.8` | | [date-fns](https://github.com/date-fns/date-fns) | `3.4.0` | `3.6.0` | | [unleash-proxy-client](https://github.com/unleash/unleash-proxy-client-js) | `3.3.1` | `3.3.2` | | @redhat-cloud-services/tsc-transform-imports | `1.0.7` | `1.0.8` | | [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `18.2.21` | `18.2.22` | Updates `@redhat-cloud-services/frontend-components` from 4.2.4 to 4.2.5 - [Commits](https://github.com/RedHatInsights/frontend-components/commits) Updates `axios` from 1.6.7 to 1.6.8 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.6.7...v1.6.8) Updates `date-fns` from 3.4.0 to 3.6.0 - [Release notes](https://github.com/date-fns/date-fns/releases) - [Changelog](https://github.com/date-fns/date-fns/blob/main/CHANGELOG.md) - [Commits](https://github.com/date-fns/date-fns/compare/v3.4.0...v3.6.0) Updates `unleash-proxy-client` from 3.3.1 to 3.3.2 - [Release notes](https://github.com/unleash/unleash-proxy-client-js/releases) - [Commits](https://github.com/unleash/unleash-proxy-client-js/compare/v3.3.1...v3.3.2) Updates `@redhat-cloud-services/tsc-transform-imports` from 1.0.7 to 1.0.8 Updates `@types/react-dom` from 18.2.21 to 18.2.22 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: "@redhat-cloud-services/frontend-components" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ci-dependencies - dependency-name: axios dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ci-dependencies - dependency-name: date-fns dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ci-dependencies - dependency-name: unleash-proxy-client dependency-type: direct:production update-type: version-update:semver-patch dependency-group: ci-dependencies - dependency-name: "@redhat-cloud-services/tsc-transform-imports" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: ci-dependencies - dependency-name: "@types/react-dom" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: ci-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 61 +++++++++++++++++++++++------------------------ package.json | 12 +++++----- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index f22765e19..9a84dcffa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,15 +17,15 @@ "@patternfly/react-icons": "^5.2.0", "@patternfly/react-table": "^5.2.0", "@patternfly/react-tokens": "^5.2.0", - "@redhat-cloud-services/frontend-components": "^4.2.4", + "@redhat-cloud-services/frontend-components": "^4.2.5", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", "@redhat-cloud-services/frontend-components-utilities": "^4.0.7", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.2.2", - "axios": "^1.6.7", - "date-fns": "^3.4.0", + "axios": "^1.6.8", + "date-fns": "^3.6.0", "js-file-download": "^0.4.12", "lodash": "^4.17.21", "qs": "^6.12.0", @@ -37,7 +37,7 @@ "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", - "unleash-proxy-client": "^3.3.1", + "unleash-proxy-client": "^3.3.2", "victory-core": "^36.9.1" }, "devDependencies": { @@ -46,7 +46,7 @@ "@formatjs/icu-messageformat-parser": "^2.7.6", "@redhat-cloud-services/eslint-config-redhat-cloud-services": "^2.0.3", "@redhat-cloud-services/frontend-components-config": "^6.0.10", - "@redhat-cloud-services/tsc-transform-imports": "^1.0.7", + "@redhat-cloud-services/tsc-transform-imports": "^1.0.8", "@swc/core": "1.3.105", "@swc/jest": "^0.2.36", "@testing-library/jest-dom": "^6.4.2", @@ -55,7 +55,7 @@ "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", "@types/react": "^18.2.63", - "@types/react-dom": "^18.2.21", + "@types/react-dom": "^18.2.22", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.1.1", @@ -2449,22 +2449,22 @@ } }, "node_modules/@redhat-cloud-services/frontend-components": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-4.2.4.tgz", - "integrity": "sha512-5xne/DD1/+txtN50YOrKPeJKpSgENkGz4o4oqYMRB+ElJwnupsaOasvyfMwImKggDcAZ/uWLXt8KNoE8ZXhVvA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components/-/frontend-components-4.2.5.tgz", + "integrity": "sha512-lTVew1R6LccTMqDoaevtk6WBbH0VoiNPspPMSuEYT+/JIQLjs5NgpBNWTGImyp/4/JFwMiQq0ewtimQLq0E3Ig==", "dependencies": { "@patternfly/react-component-groups": "^5.0.0", "@redhat-cloud-services/frontend-components-utilities": "^4.0.0", "@redhat-cloud-services/types": "^0.0.24", "@scalprum/core": "^0.7.0", "@scalprum/react-core": "^0.7.0", + "classnames": "^2.2.5", "sanitize-html": "^2.7.2" }, "peerDependencies": { "@patternfly/react-core": "^5.0.0", "@patternfly/react-icons": "^5.0.0", "@patternfly/react-table": "^5.0.0", - "classnames": "^2.2.5", "lodash": "^4.17.15", "prop-types": "^15.6.2", "react": "^18.2.0", @@ -2945,9 +2945,9 @@ } }, "node_modules/@redhat-cloud-services/tsc-transform-imports": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/tsc-transform-imports/-/tsc-transform-imports-1.0.7.tgz", - "integrity": "sha512-ps5OL6r/8jQRThydh1lzgyUcY/7jObHK5nOhk2c9Ztb6qphjDgsm4lzcWFQcPGsdneXf9swsAF4O6vvcq810yw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/tsc-transform-imports/-/tsc-transform-imports-1.0.8.tgz", + "integrity": "sha512-5QO9OC8bDU8DIPFUbbUtLP8sArv36vSAwva7F8YYRIUWe5py7GbJTqA1PSxBeD1i9wemmbLkW09gVDYJMt5peg==", "dev": true, "dependencies": { "glob": "10.3.3" @@ -4075,9 +4075,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.21.tgz", - "integrity": "sha512-gnvBA/21SA4xxqNXEwNiVcP0xSGHh/gi1VhWv9Bl46a0ItbTT5nFY+G9VSQpaG/8N/qdJpJ+vftQ4zflTtnjLw==", + "version": "18.2.22", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.22.tgz", + "integrity": "sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==", "dev": true, "dependencies": { "@types/react": "*" @@ -5847,11 +5847,11 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -6640,8 +6640,7 @@ "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "peer": true + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" }, "node_modules/clean-css": { "version": "5.3.3", @@ -7998,9 +7997,9 @@ } }, "node_modules/date-fns": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.4.0.tgz", - "integrity": "sha512-Akz4R8J9MXBsOgF1QeWeCsbv6pntT5KCPjU0Q9prBxVmWJYPLhwAIsNg3b0QAdr0ttiozYLD3L/af7Ra0jqYXw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" @@ -10966,9 +10965,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -20609,9 +20608,9 @@ } }, "node_modules/unleash-proxy-client": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/unleash-proxy-client/-/unleash-proxy-client-3.3.1.tgz", - "integrity": "sha512-tekoTRWBK+B0uG9o/5CJ+LAKNZv+OdLPFVXFESQU2JHnyjtedLyTAmdWhPIxC/eCKiBBay1/2BEWOUCJaL1vzQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/unleash-proxy-client/-/unleash-proxy-client-3.3.2.tgz", + "integrity": "sha512-ccv+85nMJAhaaE3q4BJd4Sl894kCEbYr7CwMglvuFrBhdoOGAM5s61NXksk/aA7AGXLeAzNGH5zwY5dn0H9i9A==", "dependencies": { "tiny-emitter": "^2.1.0", "uuid": "^9.0.1" diff --git a/package.json b/package.json index 7dd5f60fd..64a056716 100644 --- a/package.json +++ b/package.json @@ -57,15 +57,15 @@ "@patternfly/react-icons": "^5.2.0", "@patternfly/react-table": "^5.2.0", "@patternfly/react-tokens": "^5.2.0", - "@redhat-cloud-services/frontend-components": "^4.2.4", + "@redhat-cloud-services/frontend-components": "^4.2.5", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", "@redhat-cloud-services/frontend-components-utilities": "^4.0.7", "@redhat-cloud-services/rbac-client": "^1.2.13", "@reduxjs/toolkit": "^2.2.1", "@unleash/proxy-client-react": "^4.2.2", - "axios": "^1.6.7", - "date-fns": "^3.4.0", + "axios": "^1.6.8", + "date-fns": "^3.6.0", "js-file-download": "^0.4.12", "lodash": "^4.17.21", "qs": "^6.12.0", @@ -77,7 +77,7 @@ "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", - "unleash-proxy-client": "^3.3.1", + "unleash-proxy-client": "^3.3.2", "victory-core": "^36.9.1" }, "devDependencies": { @@ -86,7 +86,7 @@ "@formatjs/icu-messageformat-parser": "^2.7.6", "@redhat-cloud-services/eslint-config-redhat-cloud-services": "^2.0.3", "@redhat-cloud-services/frontend-components-config": "^6.0.10", - "@redhat-cloud-services/tsc-transform-imports": "^1.0.7", + "@redhat-cloud-services/tsc-transform-imports": "^1.0.8", "@swc/core": "1.3.105", "@swc/jest": "^0.2.36", "@testing-library/jest-dom": "^6.4.2", @@ -95,7 +95,7 @@ "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", "@types/react": "^18.2.63", - "@types/react-dom": "^18.2.21", + "@types/react-dom": "^18.2.22", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^7.1.1", From 53f3245ca9dfd4c6ebc66482359412c1b250296f Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 18 Mar 2024 10:12:19 -0400 Subject: [PATCH 59/61] Fix paused state --- .../ocpBreakdown/providerDetails/dataDetails/utils/status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/details/ocpBreakdown/providerDetails/dataDetails/utils/status.ts b/src/routes/details/ocpBreakdown/providerDetails/dataDetails/utils/status.ts index 3948fa676..e82b3f938 100644 --- a/src/routes/details/ocpBreakdown/providerDetails/dataDetails/utils/status.ts +++ b/src/routes/details/ocpBreakdown/providerDetails/dataDetails/utils/status.ts @@ -35,7 +35,7 @@ export const getProviderAvailability = (provider: Provider) => { if (provider.active === false && provider.paused === false) { status = StatusType.failed; // Inactive sources - } else if (provider.infrastructure.paused === true) { + } else if (provider.paused === true) { status = StatusType.paused; } else { status = StatusType.complete; From 1621ead683c76bb5186db35b3cb7d388326191ba Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 18 Mar 2024 10:25:57 -0400 Subject: [PATCH 60/61] Update dependencies --- package-lock.json | 2153 ++++++++++++++++++++++++--------------------- package.json | 26 +- 2 files changed, 1165 insertions(+), 1014 deletions(-) diff --git a/package-lock.json b/package-lock.json index 26a470032..aba5a8a46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,13 +10,13 @@ "hasInstallScript": true, "license": "GNU AGPLv3", "dependencies": { - "@patternfly/patternfly": "^5.2.0", - "@patternfly/react-charts": "^7.2.2", - "@patternfly/react-component-groups": "^5.1.0", - "@patternfly/react-core": "^5.2.0", - "@patternfly/react-icons": "^5.2.0", - "@patternfly/react-table": "^5.2.0", - "@patternfly/react-tokens": "^5.2.0", + "@patternfly/patternfly": "5.2.0", + "@patternfly/react-charts": "7.2.2", + "@patternfly/react-component-groups": "5.1.0", + "@patternfly/react-core": "5.2.0", + "@patternfly/react-icons": "5.2.0", + "@patternfly/react-table": "5.2.0", + "@patternfly/react-tokens": "5.2.0", "@redhat-cloud-services/frontend-components": "^4.2.5", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", @@ -33,19 +33,19 @@ "react-dom": "^18.2.0", "react-intl": "^6.6.2", "react-redux": "^9.1.0", - "react-router-dom": "^6.22.2", + "react-router-dom": "^6.22.3", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", "unleash-proxy-client": "^3.3.2", - "victory-core": "^36.9.1" + "victory-core": "^37.0.0" }, "devDependencies": { "@formatjs/cli": "^6.2.7", "@formatjs/ecma402-abstract": "^1.18.2", "@formatjs/icu-messageformat-parser": "^2.7.6", "@redhat-cloud-services/eslint-config-redhat-cloud-services": "^2.0.3", - "@redhat-cloud-services/frontend-components-config": "^6.0.10", + "@redhat-cloud-services/frontend-components-config": "^6.0.11", "@redhat-cloud-services/tsc-transform-imports": "^1.0.8", "@swc/core": "1.3.105", "@swc/jest": "^0.2.36", @@ -54,11 +54,11 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", - "@types/react": "^18.2.63", + "@types/react": "^18.2.67", "@types/react-dom": "^18.2.22", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", @@ -110,13 +110,13 @@ "dev": true }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -145,9 +145,9 @@ } }, "node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", + "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -155,11 +155,11 @@ "@babel/generator": "^7.23.6", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", + "@babel/helpers": "^7.24.0", + "@babel/parser": "^7.24.0", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -289,9 +289,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -349,14 +349,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", - "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", + "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", "dev": true, "dependencies": { - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -377,9 +377,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -566,9 +566,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -577,23 +577,23 @@ } }, "node_modules/@babel/template": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", - "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9" + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", - "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", + "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.23.5", @@ -602,8 +602,8 @@ "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -612,9 +612,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", @@ -2008,45 +2008,45 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -2056,9 +2056,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2130,9 +2130,9 @@ } }, "node_modules/@openshift/dynamic-plugin-sdk-webpack": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@openshift/dynamic-plugin-sdk-webpack/-/dynamic-plugin-sdk-webpack-4.0.1.tgz", - "integrity": "sha512-ZlY57t1WIl8B8XNPoq+CuU/+Ll4/ZX/7IO/dxn+7dp1S/NUmdvgwv01mXpUcjviOUhhgWl/dK2WvCQTzz6CoZg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@openshift/dynamic-plugin-sdk-webpack/-/dynamic-plugin-sdk-webpack-4.0.2.tgz", + "integrity": "sha512-zgRLt9WM63aGYygrQEtd8QtWoP5zYWr67kzlRKzGcHeRbWDgiHnY7FOiDUM04bYeb4lL6qv3iErEnrtvVpyh0Q==", "dev": true, "dependencies": { "lodash": "^4.17.21", @@ -2247,6 +2247,24 @@ "react-dom": "^17 || ^18" } }, + "node_modules/@patternfly/react-charts/node_modules/@patternfly/react-tokens": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-5.2.1.tgz", + "integrity": "sha512-8GYz/jnJTGAWUJt5eRAW5dtyiHPKETeFJBPGHaUQnvi/t1ZAkoy8i4Kd/RlHsDC7ktiu813SKCmlzwBwldAHKg==" + }, + "node_modules/@patternfly/react-charts/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, "node_modules/@patternfly/react-component-groups": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-5.1.0.tgz", @@ -2312,9 +2330,9 @@ } }, "node_modules/@patternfly/react-tokens": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-5.2.1.tgz", - "integrity": "sha512-8GYz/jnJTGAWUJt5eRAW5dtyiHPKETeFJBPGHaUQnvi/t1ZAkoy8i4Kd/RlHsDC7ktiu813SKCmlzwBwldAHKg==" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-5.2.0.tgz", + "integrity": "sha512-ZsrLpStHJQfvUJLIXT+cObJbA3jM4r9iWwULLva0s7DzznXJ6iIACQQfgwDtcSVyM95z5S1a/LHPj/wYgaqUIg==" }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", @@ -2389,9 +2407,9 @@ } }, "node_modules/@polka/url": { - "version": "1.0.0-next.24", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.24.tgz", - "integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==", + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", "dev": true }, "node_modules/@redhat-cloud-services/eslint-config-redhat-cloud-services": { @@ -2475,14 +2493,14 @@ } }, "node_modules/@redhat-cloud-services/frontend-components-config": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-config/-/frontend-components-config-6.0.10.tgz", - "integrity": "sha512-h1c72gKQVLmd52ZNQ2x95UJM2ACP1bBgct3cbNW1AyygYEMoWE1x7XYEpclT/7NMdu52L7f4mw0c0sdVusPkdA==", + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/@redhat-cloud-services/frontend-components-config/-/frontend-components-config-6.0.11.tgz", + "integrity": "sha512-HVyF5rP3YetC/9dCY7uGmVzpfKS4lLzvKx2slS/XpeZoQtcoalWwtmjmiWYm7KT148gzxIdxSP8el5H1Bc5DhA==", "dev": true, "dependencies": { "@pmmmwh/react-refresh-webpack-plugin": "^0.5.8", - "@redhat-cloud-services/frontend-components-config-utilities": "^3.0.4", - "@redhat-cloud-services/tsc-transform-imports": "^1.0.3", + "@redhat-cloud-services/frontend-components-config-utilities": "^3.0.5", + "@redhat-cloud-services/tsc-transform-imports": "^1.0.8", "@swc/core": "^1.3.76", "assert": "^2.0.0", "axios": "^0.28.0", @@ -3007,9 +3025,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz", - "integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==", + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", "engines": { "node": ">=14.0.0" } @@ -3144,9 +3162,9 @@ "dev": true }, "node_modules/@sindresorhus/merge-streams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", - "integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true, "engines": { "node": ">=18" @@ -3395,10 +3413,13 @@ } }, "node_modules/@swc/types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", - "dev": true + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.6.tgz", + "integrity": "sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } }, "node_modules/@testing-library/dom": { "version": "9.3.4", @@ -3792,9 +3813,9 @@ "integrity": "sha512-awNxydYSU+E2vL7EiOAMtiSOfL5gUM5X4YSE2A92qpxDJQ/rXz6oMPYBFDcDywlUmvIDI6zsqgq17cGm5CITQw==" }, "node_modules/@types/eslint": { - "version": "8.56.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", - "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", + "version": "8.56.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz", + "integrity": "sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==", "dev": true, "dependencies": { "@types/estree": "*", @@ -3992,9 +4013,9 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.14.202", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", - "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==" + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", + "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==" }, "node_modules/@types/mdast": { "version": "3.0.15", @@ -4018,9 +4039,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", - "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==", + "version": "20.11.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", + "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", "devOptional": true, "dependencies": { "undici-types": "~5.26.4" @@ -4065,9 +4086,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.63", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.63.tgz", - "integrity": "sha512-ppaqODhs15PYL2nGUOaOu2RSCCB4Difu4UFrP4I3NHLloXC/ESQzQMi9nvjfT1+rudd0d2L3fQPJxRSey+rGlQ==", + "version": "18.2.67", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.67.tgz", + "integrity": "sha512-vkIE2vTIMHQ/xL0rgmuoECBCkZFZeHr49HeWSc24AptMbNRo7pwSBvj73rlJJs9fGKj0koS+V7kQB1jHS0uCgw==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4128,9 +4149,9 @@ "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/send": { @@ -4209,9 +4230,9 @@ "dev": true }, "node_modules/@types/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-Hm/T0kV3ywpJyMGNbsItdivRhYNCQQf1IIsYsXnoVPES4t+FMLyDe0/K+Ea7ahWtMtSNb22ZdY7MIyoD9rqARg==", + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.5.tgz", + "integrity": "sha512-TU+fZFBTBcXj/GpDpDaBmgWk/gn96kMZ+uocaFUlV2f8a6WdMzzI44QBCmGcCiYR0Y6ZlNRiyUyKKt5nl/lbzQ==", "dev": true, "dependencies": { "source-map": "^0.6.1" @@ -4306,16 +4327,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.1.tgz", - "integrity": "sha512-zioDz623d0RHNhvx0eesUmGfIjzrk18nSBC8xewepKXbBvN/7c1qImV7Hg8TI1URTxKax7/zxfxj3Uph8Chcuw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz", + "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.1.1", - "@typescript-eslint/type-utils": "7.1.1", - "@typescript-eslint/utils": "7.1.1", - "@typescript-eslint/visitor-keys": "7.1.1", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/type-utils": "7.2.0", + "@typescript-eslint/utils": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -4340,118 +4361,6 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz", - "integrity": "sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.1.1", - "@typescript-eslint/visitor-keys": "7.1.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.1.tgz", - "integrity": "sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz", - "integrity": "sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.1.1", - "@typescript-eslint/visitor-keys": "7.1.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.1.tgz", - "integrity": "sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.1.1", - "@typescript-eslint/types": "7.1.1", - "@typescript-eslint/typescript-estree": "7.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz", - "integrity": "sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.1.1", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4513,7 +4422,7 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "node_modules/@typescript-eslint/scope-manager": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", @@ -4530,7 +4439,34 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "node_modules/@typescript-eslint/type-utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz", + "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/utils": "7.2.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", @@ -4543,7 +4479,7 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "node_modules/@typescript-eslint/typescript-estree": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", @@ -4571,36 +4507,7 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", - "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.2.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/lru-cache": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", @@ -4612,7 +4519,7 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/parser/node_modules/semver": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", @@ -4627,20 +4534,25 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/parser/node_modules/yallist": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "node_modules/@typescript-eslint/utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", + "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "semver": "^7.5.4" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4648,43 +4560,52 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.1.tgz", - "integrity": "sha512-5r4RKze6XHEEhlZnJtR3GYeCh1IueUHdbrukV2KSlLXaTjuSfeVF8mZUVPLovidCuZfbVjfhi4c0DNSa/Rdg5g==", + "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.1.1", - "@typescript-eslint/utils": "7.1.1", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "yallist": "^4.0.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" }, - "peerDependencies": { - "eslint": "^8.56.0" + "bin": { + "semver": "bin/semver.js" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": ">=10" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.1.tgz", - "integrity": "sha512-cirZpA8bJMRb4WZ+rO6+mnOJrGFDd38WoXCEI57+CYBqta8Yc8aJym2i7vyqLL1vVYljgw0X27axkUXz32T8TA==", + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.1.1", - "@typescript-eslint/visitor-keys": "7.1.1" + "@typescript-eslint/types": "7.2.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -4694,290 +4615,13 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.1.tgz", - "integrity": "sha512-KhewzrlRMrgeKm1U9bh2z5aoL4s7K3tK5DwHDn8MHv0yQfWFz/0ZR6trrIHHa5CsF83j/GgHqzdbzCXJ3crx0Q==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.1.tgz", - "integrity": "sha512-9ZOncVSfr+sMXVxxca2OJOPagRwT0u/UHikM2Rd6L/aB+kL/QAuTnsv6MeXtjzCJYb8PzrXarypSGIPx3Jemxw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.1.1", - "@typescript-eslint/visitor-keys": "7.1.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.1.tgz", - "integrity": "sha512-thOXM89xA03xAE0lW7alstvnyoBUbBX38YtY+zAUcpRPcq9EIhXPuJ0YTv948MbzmKh6e1AUszn5cBFK49Umqg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.1.1", - "@typescript-eslint/types": "7.1.1", - "@typescript-eslint/typescript-estree": "7.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.1.tgz", - "integrity": "sha512-yTdHDQxY7cSoCcAtiBzVzxleJhkGB9NncSIyMYe2+OGON1ZsP9zOPws/Pqgopa65jvknOjlk/w7ulPlZ78PiLQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.1.1", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5001,9 +4645,9 @@ } }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", @@ -5023,9 +4667,9 @@ "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { @@ -5046,15 +4690,15 @@ "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { @@ -5082,28 +4726,28 @@ "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", @@ -5111,24 +4755,24 @@ } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", @@ -5137,12 +4781,12 @@ } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, @@ -5581,16 +5225,16 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", + "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5740,15 +5384,6 @@ "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "peer": true }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6045,7 +5680,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/base64-js": { "version": "1.5.1", @@ -6100,12 +5736,15 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bl": { @@ -6156,13 +5795,13 @@ "peer": true }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -6170,7 +5809,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -6256,9 +5895,9 @@ } }, "node_modules/browserslist": { - "version": "4.22.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz", - "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "funding": [ { @@ -6275,8 +5914,8 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001580", - "electron-to-chromium": "^1.4.648", + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" }, @@ -6476,9 +6115,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001584", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001584.tgz", - "integrity": "sha512-LOz7CCQ9M1G7OjJOF9/mzmqmj3jE/7VOmrfw6Mgs0E8cjOsbRXQJHsPBfmBOXDskXKrHLyyW3n7kpDW/4BsfpQ==", + "version": "1.0.30001599", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz", + "integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==", "dev": true, "funding": [ { @@ -6570,16 +6209,10 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -6592,6 +6225,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -6997,7 +6633,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/concurrently": { "version": "7.6.0", @@ -7237,12 +6874,12 @@ } }, "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.0.tgz", - "integrity": "sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", + "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", "dev": true, "dependencies": { - "@sindresorhus/merge-streams": "^1.0.0", + "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.2", "ignore": "^5.2.4", "path-type": "^5.0.0", @@ -7315,9 +6952,9 @@ "peer": true }, "node_modules/core-js-pure": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.35.1.tgz", - "integrity": "sha512-zcIdi/CL3MWbBJYo5YCeVAAx+Sy9yJE9I3/u9LkFABwbeaPhTMRWraM8mYFp9jW5Z50hOy7FVzCc8dCrpZqtIQ==", + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.36.0.tgz", + "integrity": "sha512-cN28qmhRNgbMZZMc/RFu5w8pK9VJzpb2rJVR/lHuZJKwmXnoWOpXmMkxqBB514igkp1Hu8WGROsiOAzUcKdHOQ==", "dev": true, "hasInstallScript": true, "funding": { @@ -7355,15 +6992,6 @@ "node": ">=10" } }, - "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -7692,9 +7320,9 @@ } }, "node_modules/cypress/node_modules/@types/node": { - "version": "16.18.79", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.79.tgz", - "integrity": "sha512-Qd7jdLR5zmnIyMhfDrfPqN5tUCvreVpP3Qrf2oSM+F7SNzlb/MwHISGUkdFHtevfkPJ3iAGyeQI/jsbh9EStgQ==", + "version": "16.18.89", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.89.tgz", + "integrity": "sha512-QlrE8QI5z62nfnkiUZysUsAaxWaTMoGqFVcB3PvK1WxJ0c699bacErV4Fabe9Hki6ZnaHalgzihLbTl2d34XfQ==", "peer": true }, "node_modules/cypress/node_modules/ansi-styles": { @@ -7970,30 +7598,81 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "peer": true, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "peer": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, "dependencies": { - "assert-plus": "^1.0.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "dev": true, "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": ">=12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/date-fns": { @@ -8529,9 +8208,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.656", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.656.tgz", - "integrity": "sha512-9AQB5eFTHyR3Gvt2t/NwR0le2jBSUNwCnMbUCejFWHD+so4tH40/dRLgoE+jxlPeWS43XJewyvCv+I8LPMl49Q==", + "version": "1.4.708", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.708.tgz", + "integrity": "sha512-iWgEEvREL4GTXXHKohhh33+6Y8XkPI5eHihDmm8zUk5Zo7HICEW+wI/j5kJ2tbuNUCXJ/sNXa03ajW635DiJXA==", "dev": true }, "node_modules/emittery": { @@ -8580,9 +8259,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -8647,17 +8326,21 @@ } }, "node_modules/es-abstract": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", - "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", + "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", "es-define-property": "^1.0.0", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", @@ -8668,10 +8351,11 @@ "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.1", + "hasown": "^2.0.2", "internal-slot": "^1.0.7", "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.3", @@ -8682,17 +8366,17 @@ "object-keys": "^1.1.1", "object.assign": "^4.1.5", "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", + "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", "string.prototype.trimstart": "^1.0.7", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", "typed-array-length": "^1.0.5", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -8747,26 +8431,25 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz", - "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==", + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz", + "integrity": "sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==", "dev": true, "dependencies": { - "asynciterator.prototype": "^1.0.0", "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.4", + "es-abstract": "^1.23.0", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.2", + "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "globalthis": "^1.0.3", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.1", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", "internal-slot": "^1.0.7", "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.0" + "safe-array-concat": "^1.1.2" }, "engines": { "node": ">= 0.4" @@ -8778,6 +8461,18 @@ "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", @@ -9091,9 +8786,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -9178,6 +8873,151 @@ "eslint": "7 || 8" } }, + "node_modules/eslint-plugin-formatjs/node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-formatjs/node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-formatjs/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-formatjs/node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-formatjs/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-formatjs/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-formatjs/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-formatjs/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-formatjs/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/eslint-plugin-import": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", @@ -9253,9 +9093,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.6.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.3.tgz", - "integrity": "sha512-+YsJFVH6R+tOiO3gCJon5oqn4KWc+mDq2leudk8mrp8RFubLOo9CVyi3cib4L7XMpxExmkmBZQTPDYVBzgpgOA==", + "version": "27.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", + "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" @@ -9264,7 +9104,7 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0", + "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0", "eslint": "^7.0.0 || ^8.0.0", "jest": "*" }, @@ -10582,14 +10422,14 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.18.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz", + "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==", "dev": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.5.0", @@ -10951,9 +10791,9 @@ } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/focus-trap": { @@ -11257,7 +11097,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -11386,9 +11227,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", + "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", "dev": true, "peer": true, "dependencies": { @@ -11432,6 +11273,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11469,6 +11311,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11478,6 +11321,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -11692,9 +11536,9 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -11786,9 +11630,9 @@ } }, "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "dev": true, "funding": [ { @@ -12185,9 +12029,9 @@ } }, "node_modules/immer": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.3.tgz", - "integrity": "sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.4.tgz", + "integrity": "sha512-cuBuGK40P/sk5IzWa9QPUaAdvPHjkk1c+xYsd9oZw+YQQEV+10G0P5uMpGctZZKnyQ+ibRO08bD25nWLmYi2pw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -12255,6 +12099,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -12263,7 +12108,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "2.0.0", @@ -12598,6 +12444,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -12744,10 +12605,13 @@ } }, "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12888,10 +12752,13 @@ } }, "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12985,10 +12852,13 @@ } }, "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13006,13 +12876,16 @@ } }, "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13066,14 +12939,14 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", "dev": true, "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" }, @@ -13173,9 +13046,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -16064,9 +15937,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -16306,9 +16179,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.0.tgz", - "integrity": "sha512-CxmUYPFcTgET1zImteG/LZOy/4T5rTojesQXkSNBiquhydn78tfbCE9sjIjnJ/UcjNjOC1bphTCCW5rrS7cXAg==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", + "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", "dev": true, "dependencies": { "schema-utils": "^4.0.0", @@ -16778,13 +16651,13 @@ } }, "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -17274,6 +17147,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -17548,9 +17422,9 @@ } }, "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "version": "8.4.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.36.tgz", + "integrity": "sha512-/n7eumA6ZjFHAsbX30yhHup/IMkOmlmvtEi7P+6RMYf+bGJSUHc3geH4a0NSZxAz/RJfiS9tooCTs9LAVYUZKw==", "funding": [ { "type": "opencollective", @@ -17568,7 +17442,7 @@ "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.1.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -17634,9 +17508,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -17913,9 +17787,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { "bytes": "3.1.2", @@ -18075,11 +17949,11 @@ } }, "node_modules/react-router": { - "version": "6.22.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz", - "integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==", + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", "dependencies": { - "@remix-run/router": "1.15.2" + "@remix-run/router": "1.15.3" }, "engines": { "node": ">=14.0.0" @@ -18089,12 +17963,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.22.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz", - "integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==", + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", "dependencies": { - "@remix-run/router": "1.15.2", - "react-router": "6.22.2" + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" }, "engines": { "node": ">=14.0.0" @@ -18212,16 +18086,16 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", - "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0", - "get-intrinsic": "^1.2.3", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", "globalthis": "^1.0.3", "which-builtin-type": "^1.1.3" }, @@ -18519,13 +18393,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -18660,9 +18534,9 @@ } }, "node_modules/sass": { - "version": "1.70.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", - "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", + "version": "1.72.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.72.0.tgz", + "integrity": "sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -18930,30 +18804,31 @@ } }, "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { - "define-data-property": "^1.1.2", + "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -19129,9 +19004,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.1.0.tgz", + "integrity": "sha512-9vC2SfsJzlej6MAaMPLu8HiBSHGdRAJ9hVFYN1ibZoNkeanmDmLUcIrj6G9DGL7XMJ54AKg/G75akXl1/izTOw==", "engines": { "node": ">=0.10.0" } @@ -19215,9 +19090,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz", - "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -19231,9 +19106,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, "node_modules/spdy": { @@ -19460,14 +19335,15 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -19477,14 +19353,14 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -19657,9 +19533,9 @@ } }, "node_modules/terser": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", - "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -19865,30 +19741,12 @@ } }, "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "peer": true, - "dependencies": { - "rimraf": "^3.0.0" - }, "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/tmp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "peer": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=14.14" } }, "node_modules/tmpl": { @@ -19985,12 +19843,12 @@ } }, "node_modules/ts-api-utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.0.tgz", - "integrity": "sha512-d+3WxW4r8WQy2cZWpNRPPGExX8ffOLGcIhheUANKbL5Sqjbhkneki76fRAWeXkaslV2etTb4tSJBSxOsH5+CJw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=18" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -20824,259 +20682,514 @@ } }, "node_modules/victory-area": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-area/-/victory-area-36.9.1.tgz", - "integrity": "sha512-rElzHXJBXZ6sFkYs10aFUoUikFI48XZLbFIfL1tzdA74T426fTRQZNlKvjb2s3XL4fcecqVpvlg1I2dkaAszIQ==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-area/-/victory-area-36.9.2.tgz", + "integrity": "sha512-32aharvPf2RgdQB+/u1j3/ajYFNH/7ugLX9ZRpdd65gP6QEbtXL+58gS6CxvFw6gr/y8a0xMlkMKkpDVacXLpw==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1", - "victory-vendor": "^36.9.1" + "victory-core": "^36.9.2", + "victory-vendor": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-area/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-axis": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-axis/-/victory-axis-36.9.1.tgz", - "integrity": "sha512-s23wAFlE2KFSb6pRlmY4GXL7ZC2poL7jfUJbVWovBDkIUiz5G020ba2+RfMBL4tBTK006OPzQ3GeUPASG7qejA==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-axis/-/victory-axis-36.9.2.tgz", + "integrity": "sha512-4Odws+IAjprJtBg2b2ZCxEPgrQ6LgIOa22cFkGghzOSfTyNayN4M3AauNB44RZyn2O/hDiM1gdBkEg1g9YDevQ==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-axis/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-bar": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-36.9.1.tgz", - "integrity": "sha512-XCPKgeSBFItux1dBFpTZD90uqMw0wgd4+xD+sRgagVthTdppS3JV4YPNo1MxC/Gdm6XQfBFckcFpNG1qm3Noqw==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-bar/-/victory-bar-36.9.2.tgz", + "integrity": "sha512-R3LFoR91FzwWcnyGK2P8DHNVv9gsaWhl5pSr2KdeNtvLbZVEIvUkTeVN9RMBMzterSFPw0mbWhS1Asb3sV6PPw==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1", - "victory-vendor": "^36.9.1" + "victory-core": "^36.9.2", + "victory-vendor": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-bar/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-box-plot": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-box-plot/-/victory-box-plot-36.9.1.tgz", - "integrity": "sha512-+dSHrA1naP5xEuVeIEoRadE8VL0+QmobJ6qwTxhZyjSwR9CGOelFZEgK4oVzWb7pfSa3dYUlXQRc+UWG02zFVw==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-box-plot/-/victory-box-plot-36.9.2.tgz", + "integrity": "sha512-nUD45V/YHDkAKZyak7YDsz+Vk1F9N0ica3jWQe0AY0JqD9DleHa8RY/olSVws26kLyEj1I+fQqva6GodcLaIqQ==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1", - "victory-vendor": "^36.9.1" + "victory-core": "^36.9.2", + "victory-vendor": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-box-plot/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-brush-container": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-36.9.1.tgz", - "integrity": "sha512-XyLqCQ1LV1QbnWJh1ZlNxzk5Yp8PHqzGH6HLcnnKodZE8FBWTSREgELMQCrcT9NczI2GAA7XNkhGkZcJ4SuBMw==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-brush-container/-/victory-brush-container-36.9.2.tgz", + "integrity": "sha512-KcQjzFeo40tn52cJf1A02l5MqeR9GKkk3loDqM3T2hfi1PCyUrZXEUjGN5HNlLizDRvtcemaAHNAWlb70HbG/g==", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-brush-container/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-chart": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-36.9.1.tgz", - "integrity": "sha512-i87Ok1vAeY9LirQt6T7B8tSr7d1vAuZvVv7f1MTTlRLHEAvifBNiGrhZho5ETzvTOXOAM7UjwqzPZze0Gk66cA==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-chart/-/victory-chart-36.9.2.tgz", + "integrity": "sha512-dMNcS0BpqL3YiGvI4BSEmPR76FCksCgf3K4CSZ7C/MGyrElqB6wWwzk7afnlB1Qr71YIHXDmdwsPNAl/iEwTtA==", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-axis": "^36.9.1", - "victory-core": "^36.9.1", - "victory-polar-axis": "^36.9.1", - "victory-shared-events": "^36.9.1" + "victory-axis": "^36.9.2", + "victory-core": "^36.9.2", + "victory-polar-axis": "^36.9.2", + "victory-shared-events": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-chart/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-core": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.1.tgz", - "integrity": "sha512-voPTyOyyVipzJPjelxvszVixiI98ApMNb6X9qfaFYK7fHyavF/Hy4sf/Hwq1otatLI7zpr2hC4wF+af6HDELqA==", + "version": "37.0.0", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-37.0.0.tgz", + "integrity": "sha512-aN5ZK9OO/HukifkX942Nsr0RidL/jsi/kTsBW68Ir6c6HazsfRFp0Ee3fIxy1ikUGe2XUaOu0eBv1v06+hfJKQ==", "dependencies": { "lodash": "^4.17.21", "react-fast-compare": "^3.2.0", - "victory-vendor": "^36.9.1" + "victory-vendor": "^37.0.0" }, "peerDependencies": { "react": ">=16.6.0" } }, + "node_modules/victory-core/node_modules/victory-vendor": { + "version": "37.0.0", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.0.0.tgz", + "integrity": "sha512-jNqpyUTCw2EbNplG8KsbVkHCBPSOL5fTQHVf/1oOBecfVoO8dwAGDm0xFCAD4w4c9llck6zgIV3eqR04jVYRHA==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/victory-create-container": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-36.9.1.tgz", - "integrity": "sha512-L1c66whZAFnChVQdU2E0aYiTy3Wc1cM58V2vZPo1ORea/W9h3ojOW2bpYkG/XLf67PgnFZ299i23UzuC16Z5uw==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-create-container/-/victory-create-container-36.9.2.tgz", + "integrity": "sha512-uA0dh1R0YDzuXyE/7StZvq4qshet+WYceY7R1UR5mR/F9079xy+iQsa2Ca4h97/GtVZoLO6r1eKLWBt9TN+U7A==", "dependencies": { "lodash": "^4.17.19", - "victory-brush-container": "^36.9.1", - "victory-core": "^36.9.1", - "victory-cursor-container": "^36.9.1", - "victory-selection-container": "^36.9.1", - "victory-voronoi-container": "^36.9.1", - "victory-zoom-container": "^36.9.1" + "victory-brush-container": "^36.9.2", + "victory-core": "^36.9.2", + "victory-cursor-container": "^36.9.2", + "victory-selection-container": "^36.9.2", + "victory-voronoi-container": "^36.9.2", + "victory-zoom-container": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-create-container/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-cursor-container": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-36.9.1.tgz", - "integrity": "sha512-jAxlHbebVjIvmyUBf2AVbfk3rpQNyWPSVoozcBAzjDKhrUn5GIPvytg8QvFsShwdCtSob1eSyBEsGkb16F6xnw==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-cursor-container/-/victory-cursor-container-36.9.2.tgz", + "integrity": "sha512-jidab4j3MaciF3fGX70jTj4H9rrLcY8o2LUrhJ67ZLvEFGGmnPtph+p8Fe97Umrag7E/DszjNxQZolpwlgUh3g==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-cursor-container/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-group": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-36.9.1.tgz", - "integrity": "sha512-FJwZbrwMJSR/ucj4rYXKYJ+R6oDNsHPG2OvVs4KWkMSSp1Ld/0/V42qFqFNixcLAEcD5ACYtyigZOmS8VEnSnA==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-group/-/victory-group-36.9.2.tgz", + "integrity": "sha512-wBmpsjBTKva8mxHvHNY3b8RE58KtnpLLItEyyAHaYkmExwt3Uj8Cld3sF3vmeuijn2iR64NPKeMbgMbfZJzycw==", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.1", - "victory-shared-events": "^36.9.1" + "victory-core": "^36.9.2", + "victory-shared-events": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-group/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-legend": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-36.9.1.tgz", - "integrity": "sha512-NVWJzEJgm2+LH94b6aUQ96M58TzAgKP9wXlQC/CuYLMqK45RiLwg7pkSNuXBdtQiJgpD3W6d6klHQmUP2JkNzA==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-legend/-/victory-legend-36.9.2.tgz", + "integrity": "sha512-cucFJpv6fty+yXp5pElQFQnHBk1TqA4guGUMI+XF/wLlnuM4bhdAtASobRIIBkz0mHGBaCAAV4PzL9azPU/9dg==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-legend/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-line": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-36.9.1.tgz", - "integrity": "sha512-WfnDMI5mYN+7rC21yG3IXLIkGL+xNPAFDYikCwtKD9MnHUqk1k/HMGTH0BCVPgXagwIzd8aGpbJGlvcfRr1Kvg==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-line/-/victory-line-36.9.2.tgz", + "integrity": "sha512-kmYFZUo0o2xC8cXRsmt/oUBRQSZJVT2IJnAkboUepypoj09e6CY5tRH4TSdfEDGkBk23xQkn7d4IFgl4kAGnSA==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1", - "victory-vendor": "^36.9.1" + "victory-core": "^36.9.2", + "victory-vendor": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-line/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-pie": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-36.9.1.tgz", - "integrity": "sha512-TjfGe1Wd8cWaV7Ldd2AgPstAT0qbxY8EHYj2YyB93qfZCwdLQqxEmDobl+T+BmnRtCodXUWdghkLvVggf4N0bQ==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-pie/-/victory-pie-36.9.2.tgz", + "integrity": "sha512-i3zWezvy5wQEkhXKt4rS9ILGH7Vr9Q5eF9fKO4GMwDPBdYOTE3Dh2tVaSrfDC8g9zFIc0DKzOtVoJRTb+0AkPg==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1", - "victory-vendor": "^36.9.1" + "victory-core": "^36.9.2", + "victory-vendor": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-pie/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-polar-axis": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-polar-axis/-/victory-polar-axis-36.9.1.tgz", - "integrity": "sha512-C7oPeRzG0Mn+Veu8qI1lVgiBMyZwdrvnplUi6AnFvYf9wURoFjyC+DQ7Eh5IH4TeVQz9rr9ksiliFtXPOHCwvg==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-polar-axis/-/victory-polar-axis-36.9.2.tgz", + "integrity": "sha512-HBR90FF4M56yf/atXjSmy3DMps1vSAaLXmdVXLM/A5g+0pUS7HO719r5x6dsR3I6Rm+8x6Kk8xJs0qgpnGQIEw==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-polar-axis/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-scatter": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-36.9.1.tgz", - "integrity": "sha512-n5h/PUW2pHwiBJi0gLt5D5/jM3ZNXnFqZyjFkiKP6nztUtLRQfjcDMwmRWFOF/WZS/e2C7qMYizuXmxuU5ZVOw==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-scatter/-/victory-scatter-36.9.2.tgz", + "integrity": "sha512-hK9AtbJQfaW05i8BH7Lf1HK7vWMAfQofj23039HEQJqTKbCL77YT+Q0LhZw1a1BRCpC/5aSg9EuqblhfIYw2wg==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-scatter/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-selection-container": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-36.9.1.tgz", - "integrity": "sha512-yugHpsS+JHmhJdhduuDHIBVg0mJ60Nge52CCHdiqM7hitcK1+hJgeEPt9zyCDYivQrBimRCGjNYfXhjjCbxzrg==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-selection-container/-/victory-selection-container-36.9.2.tgz", + "integrity": "sha512-chboroEwqqVlMB60kveXM2WznJ33ZM00PWkFVCoJDzHHlYs7TCADxzhqet2S67SbZGSyvSprY2YztSxX8kZ+XQ==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-selection-container/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-shared-events": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-36.9.1.tgz", - "integrity": "sha512-U+iDeuv17qYbigMivQcYmZPrvCMHQ8oHFprrlmF9K9cby3q9NFuZ6bbZUngm8kB61P0L6gR0BbYSWvdT9QUEbA==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-shared-events/-/victory-shared-events-36.9.2.tgz", + "integrity": "sha512-W/atiw3Or6MnpBuhluFv6007YrixIRh5NtiRvtFLGxNuQJLYjaSh6koRAih5xJer5Pj7YUx0tL9x67jTRcJ6Dg==", "dependencies": { "json-stringify-safe": "^5.0.1", "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-shared-events/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-stack": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-36.9.1.tgz", - "integrity": "sha512-yTSLyq3PShJIIsHFjRZcWJvJsZU0+kZ6OhYawqnE133XkaQFdA6C4nhMGCAs6VzFT9PofzFuU0OY4geZ70G1TQ==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-stack/-/victory-stack-36.9.2.tgz", + "integrity": "sha512-imR6FniVlDFlBa/B3Est8kTryNhWj2ZNpivmVOebVDxkKcVlLaDg3LotCUOI7NzOhBQaro0UzeE9KmZV93JcYA==", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.1", - "victory-shared-events": "^36.9.1" + "victory-core": "^36.9.2", + "victory-shared-events": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-stack/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-tooltip": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-36.9.1.tgz", - "integrity": "sha512-/ICZ4jaYFplSgK1HkFikEN9d4xlRm7dI7MouYTC1m74q869nMPycLJeVjUo9RsiPnUDeiJLAnKZnXb0oICyYsQ==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-tooltip/-/victory-tooltip-36.9.2.tgz", + "integrity": "sha512-76seo4TWD1WfZHJQH87IP3tlawv38DuwrUxpnTn8+uW6/CUex82poQiVevYdmJzhataS9jjyCWv3w7pOmLBCLg==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-tooltip/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-vendor": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.1.tgz", - "integrity": "sha512-+pZIP+U3pEJdDCeFmsXwHzV7vNHQC/eIbHklfe2ZCZqayYRH7lQbHcVgsJ0XOOv27hWs4jH4MONgXxHMObTMSA==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", @@ -21095,27 +21208,53 @@ } }, "node_modules/victory-voronoi-container": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-36.9.1.tgz", - "integrity": "sha512-F/ZWvhF/JkRxFT1UPGf4HgPnBAhUmbRIBssAvsIRer4cr3p7RieMNTMcTYHtVwR9kTKClfmJKgn1T7imBGt2BA==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-voronoi-container/-/victory-voronoi-container-36.9.2.tgz", + "integrity": "sha512-NIVYqck9N4OQnEz9mgQ4wILsci3OBWWK7RLuITGHyoD7Ne/+WH1i0Pv2y9eIx+f55rc928FUTugPPhkHvXyH3A==", "dependencies": { "delaunay-find": "0.0.6", "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", - "victory-core": "^36.9.1", - "victory-tooltip": "^36.9.1" + "victory-core": "^36.9.2", + "victory-tooltip": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-voronoi-container/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" } }, "node_modules/victory-zoom-container": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-36.9.1.tgz", - "integrity": "sha512-2G+2iUsmTCpt1ItUWVOzK0CCRYCFf+/rH4uXuvXqipHjRnotz5bxOkuW68Fdx1MzGoexIc8DfQoKxKd/q0HkZA==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-zoom-container/-/victory-zoom-container-36.9.2.tgz", + "integrity": "sha512-pXa2Ji6EX/pIarKT6Hcmmu2n7IG/x8Vs0D2eACQ/nbpvZa+DXWIxCRW4hcg2Va35fmXcDIEpGaX3/soXzZ+pbw==", "dependencies": { "lodash": "^4.17.19", - "victory-core": "^36.9.1" + "victory-core": "^36.9.2" + }, + "peerDependencies": { + "react": ">=16.6.0" + } + }, + "node_modules/victory-zoom-container/node_modules/victory-core": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-core/-/victory-core-36.9.2.tgz", + "integrity": "sha512-AzmMy+9MYMaaRmmZZovc/Po9urHne3R3oX7bbXeQdVuK/uMBrlPiv11gVJnuEH2SXLVyep43jlKgaBp8ef9stQ==", + "dependencies": { + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.0", + "victory-vendor": "^36.9.2" }, "peerDependencies": { "react": ">=16.6.0" @@ -21153,9 +21292,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -21193,9 +21332,9 @@ } }, "node_modules/webpack": { - "version": "5.90.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.1.tgz", - "integrity": "sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==", + "version": "5.90.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", + "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -21723,31 +21862,34 @@ } }, "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -21925,6 +22067,15 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index 86fa166cd..4fbdce394 100644 --- a/package.json +++ b/package.json @@ -50,13 +50,13 @@ "verify": "npm-run-all build lint test" }, "dependencies": { - "@patternfly/patternfly": "^5.2.0", - "@patternfly/react-charts": "^7.2.2", - "@patternfly/react-component-groups": "^5.1.0", - "@patternfly/react-core": "^5.2.0", - "@patternfly/react-icons": "^5.2.0", - "@patternfly/react-table": "^5.2.0", - "@patternfly/react-tokens": "^5.2.0", + "@patternfly/patternfly": "5.2.0", + "@patternfly/react-charts": "7.2.2", + "@patternfly/react-component-groups": "5.1.0", + "@patternfly/react-core": "5.2.0", + "@patternfly/react-icons": "5.2.0", + "@patternfly/react-table": "5.2.0", + "@patternfly/react-tokens": "5.2.0", "@redhat-cloud-services/frontend-components": "^4.2.5", "@redhat-cloud-services/frontend-components-notifications": "^4.1.0", "@redhat-cloud-services/frontend-components-translations": "^3.2.7", @@ -73,19 +73,19 @@ "react-dom": "^18.2.0", "react-intl": "^6.6.2", "react-redux": "^9.1.0", - "react-router-dom": "^6.22.2", + "react-router-dom": "^6.22.3", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "typesafe-actions": "^5.1.0", "unleash-proxy-client": "^3.3.2", - "victory-core": "^36.9.1" + "victory-core": "^37.0.0" }, "devDependencies": { "@formatjs/cli": "^6.2.7", "@formatjs/ecma402-abstract": "^1.18.2", "@formatjs/icu-messageformat-parser": "^2.7.6", "@redhat-cloud-services/eslint-config-redhat-cloud-services": "^2.0.3", - "@redhat-cloud-services/frontend-components-config": "^6.0.10", + "@redhat-cloud-services/frontend-components-config": "^6.0.11", "@redhat-cloud-services/tsc-transform-imports": "^1.0.8", "@swc/core": "1.3.105", "@swc/jest": "^0.2.36", @@ -94,11 +94,11 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/qs": "^6.9.12", - "@types/react": "^18.2.63", + "@types/react": "^18.2.67", "@types/react-dom": "^18.2.22", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", "aphrodite": "^2.4.0", "copy-webpack-plugin": "^12.0.2", @@ -130,7 +130,7 @@ "webpack-bundle-analyzer": "^4.10.1" }, "overrides": { - "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/eslint-plugin": "^7.2.0", "redux": "^5.0.1" }, "insights": { From 6f33da8b10ed525f3377683c454131aca2bec9b6 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Mon, 18 Mar 2024 11:52:13 -0400 Subject: [PATCH 61/61] Refactor tag mapping for new data structure --- src/routes/components/dataTable/expandableTable.tsx | 2 +- src/routes/settings/tagLabels/tagMapping/tagMappingTable.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/components/dataTable/expandableTable.tsx b/src/routes/components/dataTable/expandableTable.tsx index 47af223c6..551a7fc94 100644 --- a/src/routes/components/dataTable/expandableTable.tsx +++ b/src/routes/components/dataTable/expandableTable.tsx @@ -175,7 +175,7 @@ class ExpandableTable extends React.Component - {row?.children.map((child, childIndex) => ( + {row?.children?.map((child, childIndex) => ( = ({ value: , }, ], - children: parent.children.map(child => { + children: item.children?.map(child => { return { cells: [ {}, // Empty cell for expand toggle