This is an automated email from the ASF dual-hosted git repository.

jialiang pushed a commit to branch frontend-refactor
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/frontend-refactor by this push:
     new fc6bc4ba7b AMBARI-26173: common-components for ambari admin 
implementation #3876
fc6bc4ba7b is described below

commit fc6bc4ba7b88fb581c5e199405eb6c2c81476a46
Author: vanshuhassija <[email protected]>
AuthorDate: Tue Nov 12 06:27:36 2024 +0530

    AMBARI-26173: common-components for ambari admin implementation #3876
---
 .../resources/ui/ambari-admin/package-lock.json    | 457 +++++++++++++++++++--
 .../main/resources/ui/ambari-admin/package.json    |   2 +
 .../src/components/ComboSearch/index.tsx           | 223 ++++++++++
 .../src/components/ConfirmationModal/index.tsx     |  72 ++++
 .../{Spinner.tsx => DefaultButton/index.tsx}       |  13 +-
 .../{Spinner.tsx => ErrorOverlay/index.tsx}        |  26 +-
 .../src/components/Paginator/index.tsx             | 106 +++++
 .../components/{Spinner.tsx => Spinner/index.tsx}  |   0
 .../ui/ambari-admin/src/components/Table/index.tsx | 158 +++++++
 .../components/{Spinner.tsx => Table/style.css}    |  28 +-
 .../Spinner.tsx => hooks/usePagination.ts}         |  33 +-
 11 files changed, 1056 insertions(+), 62 deletions(-)

diff --git a/ambari-admin/src/main/resources/ui/ambari-admin/package-lock.json 
b/ambari-admin/src/main/resources/ui/ambari-admin/package-lock.json
index c96905c28b..f560addcc1 100644
--- a/ambari-admin/src/main/resources/ui/ambari-admin/package-lock.json
+++ b/ambari-admin/src/main/resources/ui/ambari-admin/package-lock.json
@@ -10,6 +10,7 @@
       "dependencies": {
         "@fortawesome/free-solid-svg-icons": "^6.6.0",
         "@fortawesome/react-fontawesome": "^0.2.2",
+        "@tanstack/react-table": "^8.20.5",
         "@types/lodash": "^4.17.12",
         "bootstrap": "^5.3.3",
         "lodash": "^4.17.21",
@@ -19,6 +20,7 @@
         "react-dom": "^18.3.1",
         "react-hot-toast": "^2.4.1",
         "react-router-dom": "^5.3.4",
+        "react-select": "^5.8.3",
         "sass": "^1.77.6"
       },
       "devDependencies": {
@@ -54,7 +56,6 @@
       "version": "7.25.7",
       "resolved": 
"https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz";,
       "integrity": 
"sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/highlight": "^7.25.7",
@@ -109,7 +110,6 @@
       "version": "7.25.7",
       "resolved": 
"https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz";,
       "integrity": 
"sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/types": "^7.25.7",
@@ -142,7 +142,6 @@
       "version": "7.25.7",
       "resolved": 
"https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz";,
       "integrity": 
"sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/traverse": "^7.25.7",
@@ -199,7 +198,6 @@
       "version": "7.25.7",
       "resolved": 
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz";,
       "integrity": 
"sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6.9.0"
@@ -209,7 +207,6 @@
       "version": "7.25.7",
       "resolved": 
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz";,
       "integrity": 
"sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6.9.0"
@@ -243,7 +240,6 @@
       "version": "7.25.7",
       "resolved": 
"https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz";,
       "integrity": 
"sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/helper-validator-identifier": "^7.25.7",
@@ -259,7 +255,6 @@
       "version": "7.25.8",
       "resolved": 
"https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz";,
       "integrity": 
"sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/types": "^7.25.8"
@@ -319,7 +314,6 @@
       "version": "7.25.7",
       "resolved": 
"https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz";,
       "integrity": 
"sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/code-frame": "^7.25.7",
@@ -334,7 +328,6 @@
       "version": "7.25.7",
       "resolved": 
"https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz";,
       "integrity": 
"sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/code-frame": "^7.25.7",
@@ -353,7 +346,6 @@
       "version": "11.12.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz";,
       "integrity": 
"sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=4"
@@ -363,7 +355,6 @@
       "version": "7.25.8",
       "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz";,
       "integrity": 
"sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "@babel/helper-string-parser": "^7.25.7",
@@ -374,6 +365,138 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@emotion/babel-plugin": {
+      "version": "11.12.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz";,
+      "integrity": 
"sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.16.7",
+        "@babel/runtime": "^7.18.3",
+        "@emotion/hash": "^0.9.2",
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/serialize": "^1.2.0",
+        "babel-plugin-macros": "^3.1.0",
+        "convert-source-map": "^1.5.0",
+        "escape-string-regexp": "^4.0.0",
+        "find-root": "^1.1.0",
+        "source-map": "^0.5.7",
+        "stylis": "4.2.0"
+      }
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
+      "version": "1.9.0",
+      "resolved": 
"https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz";,
+      "integrity": 
"sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+      "license": "MIT"
+    },
+    "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": 
"https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz";,
+      "integrity": 
"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
+    "node_modules/@emotion/cache": {
+      "version": "11.13.1",
+      "resolved": 
"https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz";,
+      "integrity": 
"sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==",
+      "license": "MIT",
+      "dependencies": {
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/sheet": "^1.4.0",
+        "@emotion/utils": "^1.4.0",
+        "@emotion/weak-memoize": "^0.4.0",
+        "stylis": "4.2.0"
+      }
+    },
+    "node_modules/@emotion/hash": {
+      "version": "0.9.2",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz";,
+      "integrity": 
"sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+      "license": "MIT"
+    },
+    "node_modules/@emotion/memoize": {
+      "version": "0.9.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz";,
+      "integrity": 
"sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
+      "license": "MIT"
+    },
+    "node_modules/@emotion/react": {
+      "version": "11.13.3",
+      "resolved": 
"https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz";,
+      "integrity": 
"sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.18.3",
+        "@emotion/babel-plugin": "^11.12.0",
+        "@emotion/cache": "^11.13.0",
+        "@emotion/serialize": "^1.3.1",
+        "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
+        "@emotion/utils": "^1.4.0",
+        "@emotion/weak-memoize": "^0.4.0",
+        "hoist-non-react-statics": "^3.3.1"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@emotion/serialize": {
+      "version": "1.3.2",
+      "resolved": 
"https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz";,
+      "integrity": 
"sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==",
+      "license": "MIT",
+      "dependencies": {
+        "@emotion/hash": "^0.9.2",
+        "@emotion/memoize": "^0.9.0",
+        "@emotion/unitless": "^0.10.0",
+        "@emotion/utils": "^1.4.1",
+        "csstype": "^3.0.2"
+      }
+    },
+    "node_modules/@emotion/sheet": {
+      "version": "1.4.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz";,
+      "integrity": 
"sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
+      "license": "MIT"
+    },
+    "node_modules/@emotion/unitless": {
+      "version": "0.10.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz";,
+      "integrity": 
"sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
+      "license": "MIT"
+    },
+    "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+      "version": "1.1.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz";,
+      "integrity": 
"sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      }
+    },
+    "node_modules/@emotion/utils": {
+      "version": "1.4.1",
+      "resolved": 
"https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz";,
+      "integrity": 
"sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==",
+      "license": "MIT"
+    },
+    "node_modules/@emotion/weak-memoize": {
+      "version": "0.4.0",
+      "resolved": 
"https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz";,
+      "integrity": 
"sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
+      "license": "MIT"
+    },
     "node_modules/@esbuild/aix-ppc64": {
       "version": "0.21.5",
       "resolved": 
"https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz";,
@@ -899,6 +1022,31 @@
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
+    "node_modules/@floating-ui/core": {
+      "version": "1.6.8",
+      "resolved": 
"https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz";,
+      "integrity": 
"sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/utils": "^0.2.8"
+      }
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.6.12",
+      "resolved": 
"https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz";,
+      "integrity": 
"sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/core": "^1.6.0",
+        "@floating-ui/utils": "^0.2.8"
+      }
+    },
+    "node_modules/@floating-ui/utils": {
+      "version": "0.2.8",
+      "resolved": 
"https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz";,
+      "integrity": 
"sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==",
+      "license": "MIT"
+    },
     "node_modules/@fortawesome/fontawesome-common-types": {
       "version": "6.6.0",
       "resolved": 
"https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz";,
@@ -1002,7 +1150,6 @@
       "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,
       "license": "MIT",
       "dependencies": {
         "@jridgewell/set-array": "^1.2.1",
@@ -1017,7 +1164,6 @@
       "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,
       "license": "MIT",
       "engines": {
         "node": ">=6.0.0"
@@ -1027,7 +1173,6 @@
       "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,
       "license": "MIT",
       "engines": {
         "node": ">=6.0.0"
@@ -1037,14 +1182,12 @@
       "version": "1.5.0",
       "resolved": 
"https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz";,
       "integrity": 
"sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/@jridgewell/trace-mapping": {
       "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,
       "license": "MIT",
       "dependencies": {
         "@jridgewell/resolve-uri": "^3.1.0",
@@ -1389,6 +1532,39 @@
         "tslib": "^2.4.0"
       }
     },
+    "node_modules/@tanstack/react-table": {
+      "version": "8.20.5",
+      "resolved": 
"https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.5.tgz";,
+      "integrity": 
"sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==",
+      "license": "MIT",
+      "dependencies": {
+        "@tanstack/table-core": "8.20.5"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley";
+      },
+      "peerDependencies": {
+        "react": ">=16.8",
+        "react-dom": ">=16.8"
+      }
+    },
+    "node_modules/@tanstack/table-core": {
+      "version": "8.20.5",
+      "resolved": 
"https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz";,
+      "integrity": 
"sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley";
+      }
+    },
     "node_modules/@types/babel__core": {
       "version": "7.20.5",
       "resolved": 
"https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz";,
@@ -1461,6 +1637,12 @@
       "integrity": 
"sha512-sviUmCE8AYdaF/KIHLDJBQgeYzPBI0vf/17NaYehBJfYD1j6/L95Slh07NlyK2iNyBNaEkb3En2jRt+a8y3xZQ==",
       "license": "MIT"
     },
+    "node_modules/@types/parse-json": {
+      "version": "4.0.2",
+      "resolved": 
"https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz";,
+      "integrity": 
"sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
+      "license": "MIT"
+    },
     "node_modules/@types/prop-types": {
       "version": "15.7.13",
       "resolved": 
"https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz";,
@@ -1831,7 +2013,6 @@
       "version": "3.2.1",
       "resolved": 
"https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz";,
       "integrity": 
"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "color-convert": "^1.9.0"
@@ -1860,6 +2041,21 @@
       "dev": true,
       "license": "Python-2.0"
     },
+    "node_modules/babel-plugin-macros": {
+      "version": "3.1.0",
+      "resolved": 
"https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz";,
+      "integrity": 
"sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.12.5",
+        "cosmiconfig": "^7.0.0",
+        "resolve": "^1.19.0"
+      },
+      "engines": {
+        "node": ">=10",
+        "npm": ">=6"
+      }
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": 
"https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz";,
@@ -1958,7 +2154,6 @@
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz";,
       "integrity": 
"sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=6"
@@ -1989,7 +2184,6 @@
       "version": "2.4.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz";,
       "integrity": 
"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-styles": "^3.2.1",
@@ -2046,7 +2240,6 @@
       "version": "1.9.3",
       "resolved": 
"https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz";,
       "integrity": 
"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "color-name": "1.1.3"
@@ -2056,7 +2249,6 @@
       "version": "1.1.3",
       "resolved": 
"https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz";,
       "integrity": 
"sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/concat-map": {
@@ -2073,6 +2265,22 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/cosmiconfig": {
+      "version": "7.1.0",
+      "resolved": 
"https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz";,
+      "integrity": 
"sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.2.1",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.10.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/cross-spawn": {
       "version": "7.0.3",
       "resolved": 
"https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz";,
@@ -2098,7 +2306,6 @@
       "version": "4.3.7",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz";,
       "integrity": 
"sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ms": "^2.1.3"
@@ -2145,6 +2352,15 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz";,
+      "integrity": 
"sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "license": "MIT",
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
     "node_modules/esbuild": {
       "version": "0.21.5",
       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz";,
@@ -2198,7 +2414,6 @@
       "version": "1.0.5",
       "resolved": 
"https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz";,
       "integrity": 
"sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.8.0"
@@ -2557,6 +2772,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/find-root": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz";,
+      "integrity": 
"sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+      "license": "MIT"
+    },
     "node_modules/find-up": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz";,
@@ -2609,6 +2830,15 @@
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
     },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": 
"https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz";,
+      "integrity": 
"sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
     "node_modules/gensync": {
       "version": "1.0.0-beta.2",
       "resolved": 
"https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz";,
@@ -2665,12 +2895,23 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz";,
       "integrity": 
"sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=4"
       }
     },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz";,
+      "integrity": 
"sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/history": {
       "version": "4.10.1",
       "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz";,
@@ -2714,7 +2955,6 @@
       "version": "3.3.0",
       "resolved": 
"https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz";,
       "integrity": 
"sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "parent-module": "^1.0.0",
@@ -2752,6 +2992,12 @@
         "loose-envify": "^1.0.0"
       }
     },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": 
"https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz";,
+      "integrity": 
"sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+      "license": "MIT"
+    },
     "node_modules/is-binary-path": {
       "version": "2.1.0",
       "resolved": 
"https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz";,
@@ -2764,6 +3010,21 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-core-module": {
+      "version": "2.15.1",
+      "resolved": 
"https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz";,
+      "integrity": 
"sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
+      "license": "MIT",
+      "dependencies": {
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": 
"https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz";,
@@ -2830,7 +3091,6 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz";,
       "integrity": 
"sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
-      "dev": true,
       "license": "MIT",
       "bin": {
         "jsesc": "bin/jsesc"
@@ -2846,6 +3106,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": 
"https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz";,
+      "integrity": 
"sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "license": "MIT"
+    },
     "node_modules/json-schema-traverse": {
       "version": "0.4.1",
       "resolved": 
"https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz";,
@@ -2897,6 +3163,12 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": 
"https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz";,
+      "integrity": 
"sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "license": "MIT"
+    },
     "node_modules/locate-path": {
       "version": "6.0.0",
       "resolved": 
"https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz";,
@@ -2948,6 +3220,12 @@
         "yallist": "^3.0.2"
       }
     },
+    "node_modules/memoize-one": {
+      "version": "6.0.0",
+      "resolved": 
"https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz";,
+      "integrity": 
"sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
+      "license": "MIT"
+    },
     "node_modules/merge2": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz";,
@@ -2989,7 +3267,6 @@
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz";,
       "integrity": 
"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/nanoid": {
@@ -3097,7 +3374,6 @@
       "version": "1.0.1",
       "resolved": 
"https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz";,
       "integrity": 
"sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "callsites": "^3.0.0"
@@ -3106,6 +3382,24 @@
         "node": ">=6"
       }
     },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": 
"https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz";,
+      "integrity": 
"sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus";
+      }
+    },
     "node_modules/path": {
       "version": "0.12.7",
       "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz";,
@@ -3136,6 +3430,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": 
"https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz";,
+      "integrity": 
"sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "license": "MIT"
+    },
     "node_modules/path-to-regexp": {
       "version": "1.9.0",
       "resolved": 
"https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz";,
@@ -3145,11 +3445,19 @@
         "isarray": "0.0.1"
       }
     },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz";,
+      "integrity": 
"sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/picocolors": {
       "version": "1.1.1",
       "resolved": 
"https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz";,
       "integrity": 
"sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
-      "dev": true,
       "license": "ISC"
     },
     "node_modules/picomatch": {
@@ -3398,6 +3706,27 @@
         "react": ">=15"
       }
     },
+    "node_modules/react-select": {
+      "version": "5.8.3",
+      "resolved": 
"https://registry.npmjs.org/react-select/-/react-select-5.8.3.tgz";,
+      "integrity": 
"sha512-lVswnIq8/iTj1db7XCG74M/3fbGB6ZaluCzvwPGT5ZOjCdL/k0CLWhEK0vCBLuU5bHTEf6Gj8jtSvi+3v+tO1w==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.12.0",
+        "@emotion/cache": "^11.4.0",
+        "@emotion/react": "^11.8.1",
+        "@floating-ui/dom": "^1.0.1",
+        "@types/react-transition-group": "^4.4.0",
+        "memoize-one": "^6.0.0",
+        "prop-types": "^15.6.0",
+        "react-transition-group": "^4.3.0",
+        "use-isomorphic-layout-effect": "^1.1.2"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/react-transition-group": {
       "version": "4.4.5",
       "resolved": 
"https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz";,
@@ -3432,11 +3761,27 @@
       "integrity": 
"sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
       "license": "MIT"
     },
+    "node_modules/resolve": {
+      "version": "1.22.8",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz";,
+      "integrity": 
"sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+      "license": "MIT",
+      "dependencies": {
+        "is-core-module": "^2.13.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
     "node_modules/resolve-from": {
       "version": "4.0.0",
       "resolved": 
"https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz";,
       "integrity": 
"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=4"
@@ -3578,6 +3923,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/source-map": {
+      "version": "0.5.7",
+      "resolved": 
"https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz";,
+      "integrity": 
"sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/source-map-js": {
       "version": "1.2.1",
       "resolved": 
"https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz";,
@@ -3600,11 +3954,16 @@
         "url": "https://github.com/sponsors/sindresorhus";
       }
     },
+    "node_modules/stylis": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz";,
+      "integrity": 
"sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+      "license": "MIT"
+    },
     "node_modules/supports-color": {
       "version": "5.5.0",
       "resolved": 
"https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz";,
       "integrity": 
"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "has-flag": "^3.0.0"
@@ -3613,6 +3972,18 @@
         "node": ">=4"
       }
     },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": 
"https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz";,
+      "integrity": 
"sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
     "node_modules/text-table": {
       "version": "0.2.0",
       "resolved": 
"https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz";,
@@ -3636,7 +4007,6 @@
       "version": "2.0.0",
       "resolved": 
"https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz";,
       "integrity": 
"sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=4"
@@ -3780,6 +4150,20 @@
         "punycode": "^2.1.0"
       }
     },
+    "node_modules/use-isomorphic-layout-effect": {
+      "version": "1.1.2",
+      "resolved": 
"https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz";,
+      "integrity": 
"sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==",
+      "license": "MIT",
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/util": {
       "version": "0.10.4",
       "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz";,
@@ -3897,6 +4281,15 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/yaml": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz";,
+      "integrity": 
"sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/yocto-queue": {
       "version": "0.1.0",
       "resolved": 
"https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz";,
diff --git a/ambari-admin/src/main/resources/ui/ambari-admin/package.json 
b/ambari-admin/src/main/resources/ui/ambari-admin/package.json
index a963bb51b4..3b406b6ba1 100644
--- a/ambari-admin/src/main/resources/ui/ambari-admin/package.json
+++ b/ambari-admin/src/main/resources/ui/ambari-admin/package.json
@@ -12,6 +12,7 @@
   "dependencies": {
     "@fortawesome/free-solid-svg-icons": "^6.6.0",
     "@fortawesome/react-fontawesome": "^0.2.2",
+    "@tanstack/react-table": "^8.20.5",
     "@types/lodash": "^4.17.12",
     "bootstrap": "^5.3.3",
     "lodash": "^4.17.21",
@@ -21,6 +22,7 @@
     "react-dom": "^18.3.1",
     "react-hot-toast": "^2.4.1",
     "react-router-dom": "^5.3.4",
+    "react-select": "^5.8.3",
     "sass": "^1.77.6"
   },
   "devDependencies": {
diff --git 
a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/ComboSearch/index.tsx
 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/ComboSearch/index.tsx
new file mode 100644
index 0000000000..83bd75292e
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/ComboSearch/index.tsx
@@ -0,0 +1,223 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* eslint-disable @typescript-eslint/ban-types */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { useEffect, useState } from "react";
+import Select, { SingleValue } from "react-select";
+import { get } from "lodash";
+import { Badge, Button } from "react-bootstrap";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faClose } from "@fortawesome/free-solid-svg-icons";
+type FilterField = { label: string; value: string };
+type PropTypes = {
+  fields: FilterField[];
+  data: any;
+  valueMappings?: { [key: string]: string };
+  searchCallback: Function;
+};
+function ComboSearch({ fields, data, searchCallback }: PropTypes) {
+  const [selectedFilters, setSelectedFilters] = useState<
+    { field: FilterField; value: FilterField }[]
+  >([]);
+  const [selectedField, setSelectedField] = useState<FilterField>(
+    {} as FilterField
+  );
+  const [selectedValue, setSelectedValue] = useState<FilterField>(
+    {} as FilterField
+  );
+  const [valueOptions, setValueOptions] = useState<FilterField[]>([]);
+  function getCorrespondingValues(){
+    const mappedKey = selectedField.value;
+      let allValues: any[] = [];
+      data.forEach((item: any) => {
+        const value = get(item, mappedKey, "");
+        if (!value) {
+          // Ignore empty value
+          return;
+        }
+        if (Array.isArray(value)) {
+          allValues = [...allValues, ...value];
+        } else if (typeof value !== "object") {
+          allValues.push(value);
+        }
+      });
+      const uniqueValues = [...new Set(allValues)];
+      console.log("Unique values", uniqueValues);
+      const correspondingValues = uniqueValues.filter(item=>{
+        return 
selectedValue?.value!==item&&!!!selectedFilters?.find(fil=>fil.value.value===item)
+      }).map((item: any) => {
+        return {
+          label: item,
+          value: item,
+        };
+      });
+      return correspondingValues;
+  }
+  // START GENAI@CHATGPT4
+  useEffect(() => {
+    if (selectedField) {
+      const correspondingValues = getCorrespondingValues();
+      setSelectedValue({} as FilterField);
+      setValueOptions(correspondingValues);
+    }
+  }, [selectedField]);
+
+  useEffect(()=>{
+    if(selectedValue){
+      const correspondingValues=getCorrespondingValues();
+      setValueOptions(correspondingValues);
+    }
+  },[selectedValue,selectedFilters])
+
+  const filterData = (data: any) => {
+    const categoryFilters: { [key: string]: any } = {};
+
+    selectedFilters.forEach((filter) => {
+      const category = filter.field.value;
+      if (!categoryFilters[category]) {
+        categoryFilters[category] = [];
+      }
+      categoryFilters[category].push(filter.value.value);
+    });
+
+    const filteredData = data.filter((item: any) => {
+      return Object.keys(categoryFilters).every((category) => {
+        // Apply OR logic within the same category
+        const itemValue = get(item, category);
+        return Array.isArray(itemValue)
+          ? itemValue.some((v) => categoryFilters[category].includes(v))
+          : categoryFilters[category].includes(itemValue);
+      });
+    });
+    return filteredData;
+  };
+  useEffect(() => {
+    if (selectedFilters.length) {
+      searchCallback(filterData(data));
+    } else {
+      searchCallback(data);
+    }
+    console.log("Selected Filters", selectedFilters);
+  }, [selectedFilters.length]);
+  function addFilter(e: any) {
+    e.preventDefault();
+    const newFilter = { field: selectedField, value: selectedValue };
+    if (
+      !selectedFilters.some(
+        (filter) =>
+          filter.field.value === newFilter.field.value &&
+          filter.value.value === newFilter.value.value
+      )
+    ) {
+      setSelectedFilters([...selectedFilters, newFilter]);
+      setSelectedField(null as any);
+      setSelectedValue(null as any)
+    }
+  }
+  function deleteFilter(filterToDelete: {
+    field: { label: string; value: any };
+    value: { label: string; value: any };
+  }) {
+    setSelectedFilters((prevFilters) => {
+      return prevFilters.filter((filter) => {
+        return !(
+          filter.field.value === filterToDelete.field.value &&
+          filter.value.value === filterToDelete.value.value
+        );
+      });
+    });
+  }
+  function resetFilters() {
+    setSelectedField(null as any);
+    setSelectedValue(null as any);
+    setSelectedFilters([]);
+  }
+  return (
+    <div className="d-flex w-100 flex-column ease show" 
data-testid="search-filters">
+      <div className="text-muted">
+        Select filter(s) to tailor your search. Records update immediately to
+        reflect your preferences.
+      </div>
+      <div className="d-flex mt-2">
+        <form onSubmit={addFilter} className="d-flex w-100 align-items-center">
+          <Select
+            value={selectedField}
+            menuPortalTarget={document.body} 
+            options={fields}
+            placeholder="Select field"
+            className="w-25"
+            onChange={(value:SingleValue<FilterField>) => {
+              setSelectedField(value as FilterField);
+            }}
+          ></Select>
+          <Select
+            className="ms-2 w-25"
+            menuPortalTarget={document.body} 
+            value={selectedValue}
+            options={valueOptions}
+            placeholder="Value"
+            onChange={(value) => {
+              setSelectedValue(value as FilterField);
+            }}
+          ></Select>
+          <Button
+            disabled={!selectedField?.label || !selectedValue?.value}
+            size="sm"
+            variant="outline-secondary"
+            onClick={addFilter}
+            type="submit"
+            className="ms-2"
+          >
+            Add Filter
+          </Button>
+          <Button
+            size="sm"
+            variant="outline-danger"
+            onClick={resetFilters}
+            className="ms-2"
+          >
+            Reset Filters
+          </Button>
+        </form>
+      </div>
+      <div className="mt-2 d-flex flex-wrap">
+        {selectedFilters.map((fil, index) => {
+          return (
+            <Badge
+              bg={`secondary d-flex mt-2  align-items-center text-white ${
+                index > 0 ? "ms-2" : ""
+              }`}
+            >
+              <div className="text-white">{fil.field.label}:</div>
+              <div className="ms-2 text-white">{fil.value.label}</div>
+              <FontAwesomeIcon
+                icon={faClose}
+                onClick={() => {
+                  deleteFilter(fil);
+                }}
+                className="delete-filter cursot-pointer ms-2"
+              />
+            </Badge>
+          );
+        })}
+      </div>
+    </div>
+  );
+}
+
+export default ComboSearch;
diff --git 
a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/ConfirmationModal/index.tsx
 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/ConfirmationModal/index.tsx
new file mode 100644
index 0000000000..9fab2da1d1
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/ConfirmationModal/index.tsx
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Button, Modal } from "react-bootstrap";
+import DefaultButton from "../DefaultButton";
+
+type ConfirmationModalProps = {
+  isOpen: boolean;
+  onClose: () => void;
+  modalTitle: string;
+  modalBody: string;
+  successCallback: () => void;
+  buttonVariant?: string;
+};
+
+export default function ConfirmationModal({
+  isOpen,
+  onClose,
+  modalTitle,
+  modalBody,
+  successCallback,
+  buttonVariant = "success",
+}: ConfirmationModalProps) {
+  return (
+    <Modal
+      show={isOpen}
+      onHide={onClose}
+      size="lg"
+      className="custom-modal-container modal-width"
+      data-testid="confirmation-modal"
+    >
+      <Modal.Header>
+        <Modal.Title>
+          <h3>{modalTitle}</h3>
+        </Modal.Title>
+      </Modal.Header>
+      <Modal.Body>{modalBody}</Modal.Body>
+      <Modal.Footer className="d-flex justify-content-end">
+        <DefaultButton
+          size="sm"
+          onClick={onClose}
+          data-testid="confirm-cancel-btn"
+        >
+          CANCEL
+        </DefaultButton>
+        <Button
+          className="custom-btn"
+          variant={buttonVariant}
+          onClick={successCallback}
+          size="sm"
+          data-testid="confirm-ok-btn"
+        >
+          OK
+        </Button>
+      </Modal.Footer>
+    </Modal>
+  );
+}
diff --git 
a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/DefaultButton/index.tsx
similarity index 75%
copy from 
ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx
copy to 
ambari-admin/src/main/resources/ui/ambari-admin/src/components/DefaultButton/index.tsx
index c4a47c2ea2..9bc09cfa86 100644
--- a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx
+++ 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/DefaultButton/index.tsx
@@ -15,12 +15,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Spinner as DefaultSpinner } from "react-bootstrap";
+import { Button } from "react-bootstrap";
 
-export default function Spinner() {
-  return (
-    <div className="d-flex justify-content-center align-items-center m-5" 
data-testid="admin-spinner">
-      <DefaultSpinner />
-    </div>
-  );
+function DefaultButton({ className, ...props }: any) {
+  className = className ? className + " btn-default" : "btn-default";
+  return <Button className={className} {...props}></Button>;
 }
+
+export default DefaultButton;
\ No newline at end of file
diff --git 
a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/ErrorOverlay/index.tsx
similarity index 63%
copy from 
ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx
copy to 
ambari-admin/src/main/resources/ui/ambari-admin/src/components/ErrorOverlay/index.tsx
index c4a47c2ea2..cd39597484 100644
--- a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx
+++ 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/ErrorOverlay/index.tsx
@@ -15,12 +15,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Spinner as DefaultSpinner } from "react-bootstrap";
+import { Overlay, Tooltip } from "react-bootstrap";
 
-export default function Spinner() {
-  return (
-    <div className="d-flex justify-content-center align-items-center m-5" 
data-testid="admin-spinner">
-      <DefaultSpinner />
-    </div>
-  );
+type ErrorOverlayProps = {
+    target: React.RefObject<HTMLElement>;
+    showTooltip: boolean;
+    errorMessage: string;
 }
+
+export default function ErrorOverlay({target, showTooltip, errorMessage}: 
ErrorOverlayProps) {
+    return <Overlay
+    target={target.current}
+    show={showTooltip}
+    placement="top"
+  >
+    {(props) => (
+      <Tooltip {...props}>
+        {errorMessage}
+      </Tooltip>
+    )}
+  </Overlay>
+}
\ No newline at end of file
diff --git 
a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Paginator/index.tsx
 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Paginator/index.tsx
new file mode 100644
index 0000000000..b38ba54c1d
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Paginator/index.tsx
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Dropdown, DropdownButton, Pagination } from "react-bootstrap";
+
+type PaginatorProps = {
+  currentPage: number;
+  maxPage: number;
+  changePage: (pageNumber: number) => void;
+  itemsPerPage: number;
+  setItemsPerPage: (recordsCount: number) => void;
+  totalItems: number;
+};
+
+const Paginator = ({
+  currentPage,
+  maxPage,
+  changePage,
+  itemsPerPage,
+  setItemsPerPage,
+  totalItems,
+}: PaginatorProps) => {
+  const items = [];
+  const perPageOptions = [10, 25, 50, 100];
+  for (let number = 1; number <= maxPage; number++) {
+    if (
+      number === currentPage - 1 ||
+      number === currentPage ||
+      number === currentPage + 1 ||
+      number === 1 ||
+      number === maxPage
+    ) {
+      items.push(
+        <Pagination.Item
+          key={number}
+          active={number === currentPage}
+          onClick={() => changePage(number)}
+          className="pagination-btn"
+        >
+          {number}
+        </Pagination.Item>
+      );
+    } else if (number === currentPage - 2 || number === currentPage + 2) {
+      items.push(<Pagination.Ellipsis />);
+    }
+  }
+  const firstItemIndex = (currentPage - 1) * itemsPerPage + 1;
+  const lastItemIndex = Math.min(currentPage * itemsPerPage, totalItems);
+  return (
+    <>
+      {totalItems > 10 ? (
+        <div className="mt-4 border p-3 py-0" data-testid="pagination">
+          <div className="d-flex justify-content-between align-items-center 
py-0">
+            <div>
+              Showing {firstItemIndex}-{lastItemIndex} of {totalItems} items
+            </div>
+            <div className="mt-3 d-flex">
+              <DropdownButton title={itemsPerPage} size="sm" variant="light">
+                {perPageOptions.map((perPageOption) => {
+                  return (
+                    <Dropdown.Item
+                      key={perPageOption}
+                      onClick={() => {
+                        setItemsPerPage(perPageOption);
+                      }}
+                    >
+                      {perPageOption}
+                    </Dropdown.Item>
+                  );
+                })}
+              </DropdownButton>
+              <Pagination>
+                <Pagination.Prev
+                  title="Previous"
+                  className="ms-1"
+                  onClick={() => changePage(currentPage - 1)}
+                />
+                {items}
+                <Pagination.Next
+                  className="ms-1"
+                  onClick={() => changePage(currentPage + 1)}
+                />
+              </Pagination>
+            </div>
+          </div>
+        </div>
+      ) : null}
+    </>
+  );
+};
+
+export default Paginator;
diff --git 
a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner/index.tsx
similarity index 100%
copy from 
ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx
copy to 
ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner/index.tsx
diff --git 
a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Table/index.tsx
 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Table/index.tsx
new file mode 100644
index 0000000000..6b8bb09dc8
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Table/index.tsx
@@ -0,0 +1,158 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import React from "react";
+import {
+  ColumnDef,
+  flexRender,
+  getCoreRowModel,
+  getSortedRowModel,
+  OnChangeFn,
+  SortingState,
+  useReactTable,
+} from "@tanstack/react-table";
+import { Table as BootstrapTable } from "react-bootstrap";
+import "./style.css";
+import { get } from "lodash";
+interface TableProps {
+  columns: ColumnDef<unknown, unknown>[];
+  data: unknown[];
+  onSortingChange?: OnChangeFn<SortingState>;
+  sorting?: SortingState;
+  className?: string;
+  restProps?: any;
+  striped?: boolean;
+  bordered?: boolean;
+  hover?: boolean;
+  entityName?: string;
+}
+
+const Table: React.FC<TableProps> = ({
+  columns,
+  data,
+  sorting,
+  onSortingChange,
+  entityName,
+  ...restProps
+}) => {
+  const table = useReactTable({
+    columns,
+    data,
+    debugTable: true,
+    getCoreRowModel: getCoreRowModel(),
+    getSortedRowModel: getSortedRowModel(), //client-side sorting
+    onSortingChange,
+    state: {
+      sorting,
+    },
+  });
+
+  if (!data.length && entityName) {
+    return (
+      <div className="d-flex justify-content-center">
+        <h4>NO {entityName.toUpperCase()} TO DISPLAY.</h4>
+      </div>
+    );
+  }
+
+  return (
+    <div>
+      <BootstrapTable responsive {...restProps}>
+        <thead>
+          {table.getHeaderGroups().map((headerGroup) => (
+            <tr key={headerGroup.id}>
+              {headerGroup.headers.map((header) => {
+                return (
+                  <th key={header.id} colSpan={header.colSpan}>
+                    {header.isPlaceholder ? null : (
+                      <div
+                        className={
+                          header.column.getCanSort()
+                            ? "cursor-pointer select-none"
+                            : ""
+                        }
+                        onClick={() => {
+                          if (!header.column.getCanSort()) return;
+                          onSortingChange?.([
+                            {
+                              id: header.id,
+                              desc:
+                                sorting?.[0].id === header.id
+                                  ? !sorting[0].desc
+                                  : false,
+                            },
+                          ]);
+                          header.column.getToggleSortingHandler();
+                        }}
+                        title={
+                          header.column.getCanSort()
+                            ? header.column.getNextSortingOrder() === "asc"
+                              ? "Sort ascending"
+                              : header.column.getNextSortingOrder() === "desc"
+                              ? "Sort descending"
+                              : "Clear sort"
+                            : undefined
+                        }
+                      >
+                        {flexRender(
+                          header.column.columnDef.header,
+                          header.getContext()
+                        )}
+                        {{
+                          asc: "a",
+                          desc: "d",
+                        }[header.column.getIsSorted() as string] ?? null}
+                      </div>
+                    )}
+                  </th>
+                );
+              })}
+            </tr>
+          ))}
+        </thead>
+        <tbody>
+          {table.getRowModel().rows.map((row) => {
+            return (
+              <tr key={row.id} className="text-break" role="listitem">
+                {row.getVisibleCells().map((cell) => {
+                  return (
+                    <td
+                      key={cell.id}
+                      style={{
+                        width: get(cell, "column.columnDef.width", "auto")
+                      }}
+                    >
+                      <div>
+                        {flexRender(
+                          cell.column.columnDef.cell,
+                          cell.getContext()
+                        )}
+                      </div>
+                    </td>
+                  );
+                })}
+              </tr>
+            );
+          })}
+        </tbody>
+      </BootstrapTable>
+    </div>
+  );
+};
+
+export default Table;
diff --git 
a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Table/style.css
similarity index 67%
copy from 
ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx
copy to 
ambari-admin/src/main/resources/ui/ambari-admin/src/components/Table/style.css
index c4a47c2ea2..eaaed4f923 100644
--- a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx
+++ 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Table/style.css
@@ -15,12 +15,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Spinner as DefaultSpinner } from "react-bootstrap";
-
-export default function Spinner() {
-  return (
-    <div className="d-flex justify-content-center align-items-center m-5" 
data-testid="admin-spinner">
-      <DefaultSpinner />
-    </div>
-  );
-}
+ .table thead,
+ .table thead th {
+   color: #999 !important;
+   font-weight: 700 !important;
+ }
+ .table tbody td {
+   color: #666 !important;
+ }
+ .table tbody td:hover {
+   cursor: default;
+ }
+ .table > thead > tr > th,
+ .table > tbody > tr > th,
+ .table > tfoot > tr > th,
+ .table > thead > tr > td,
+ .table > tbody > tr > td,
+ .table > tfoot > tr > td {
+   padding: 12px!important;
+ }
\ No newline at end of file
diff --git 
a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx 
b/ambari-admin/src/main/resources/ui/ambari-admin/src/hooks/usePagination.ts
similarity index 54%
rename from 
ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx
rename to 
ambari-admin/src/main/resources/ui/ambari-admin/src/hooks/usePagination.ts
index c4a47c2ea2..794e0f389d 100644
--- a/ambari-admin/src/main/resources/ui/ambari-admin/src/components/Spinner.tsx
+++ b/ambari-admin/src/main/resources/ui/ambari-admin/src/hooks/usePagination.ts
@@ -15,12 +15,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Spinner as DefaultSpinner } from "react-bootstrap";
+import { useState } from 'react';
 
-export default function Spinner() {
-  return (
-    <div className="d-flex justify-content-center align-items-center m-5" 
data-testid="admin-spinner">
-      <DefaultSpinner />
-    </div>
+const usePagination = (items:any, initialItemsPerPage=10) => {
+  const [currentPage, setCurrentPage] = useState(1);
+  const [itemsPerPage, setItemsPerPage] = useState(initialItemsPerPage);
+
+  const maxPage = Math.ceil(items.length / itemsPerPage);
+
+  const currentItems = items.slice(
+    (currentPage - 1) * itemsPerPage,
+    currentPage * itemsPerPage
   );
-}
+
+  const changePage = (newPage:number) => {
+    const safePage = Math.max(1, Math.min(newPage, maxPage));
+    setCurrentPage(safePage);
+  };
+
+  return {
+    currentItems,
+    changePage,
+    currentPage,
+    maxPage,
+    itemsPerPage,
+    setItemsPerPage
+  };
+};
+export default usePagination;
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to