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

arshad 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 d898795278 AMBARI-26389: Ambari Web React: Implement filter in list 
Version (#4066)
d898795278 is described below

commit d898795278270705388a752ccec182bef58e8b5b
Author: Sandeep  Kumar <[email protected]>
AuthorDate: Sat Sep 13 22:44:00 2025 +0530

    AMBARI-26389: Ambari Web React: Implement filter in list Version (#4066)
---
 ambari-web/latest/src/api/VersionsApi.ts           |   8 ++
 .../ClusterAdmin/StackAndVersions/ListVersion.tsx  | 142 +++++++++++++++++++--
 2 files changed, 140 insertions(+), 10 deletions(-)

diff --git a/ambari-web/latest/src/api/VersionsApi.ts 
b/ambari-web/latest/src/api/VersionsApi.ts
index 924f5ca481..b911562307 100644
--- a/ambari-web/latest/src/api/VersionsApi.ts
+++ b/ambari-web/latest/src/api/VersionsApi.ts
@@ -26,6 +26,14 @@ const VersionsApi = {
     });
     return response.data;
   },
+  getAllStacks: async function (clusterName: string) {
+    const url = 
`/clusters/${clusterName}/stack_versions?fields=*,repository_versions/*,repository_versions/operating_systems/OperatingSystems/*,repository_versions/operating_systems/repositories/*`;
+    const response = await ambariApi.request({
+      url: url,
+      method: "GET",
+    });
+    return response.data;
+  }, 
 };
 
 export default VersionsApi;
diff --git 
a/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListVersion.tsx 
b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListVersion.tsx
index c91b075307..3dca8a5e22 100644
--- 
a/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListVersion.tsx
+++ 
b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListVersion.tsx
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-import { useContext, useRef, useState } from "react";
+import { useContext, useEffect, useRef, useState } from "react";
 import {
   Button,
   ButtonGroup,
@@ -31,6 +31,7 @@ import toast from "react-hot-toast";
 import Spinner from "../../../components/Spinner";
 import Table from "../../../components/Table";
 import { get } from "lodash";
+import Select from "react-select";
 import Modal from "../../../components/Modal";
 import usePolling from "../../../hooks/usePolling";
 import { Link } from "react-router-dom";
@@ -39,14 +40,36 @@ import { RequestApi } from "../../../api/requestApi";
 import OperationsProgress from "../../../components/OperationProgress";
 import { StackVersion } from "./types";
 
+const initialOptions = [
+  { key: "ALL", values: ["ALL"], count: 0 },
+  {
+    key: "NOT INSTALLED",
+    values: ["INSTALL_FAILED", "INSTALLING", "NOT_REQUIRED"],
+    count: 0,
+  },
+  { key: "UPGRADE READY", values: ["UPGRADE_READY"], count: 0 },
+  { key: "CURRENT", values: ["CURRENT"], count: 0 },
+  { key: "INSTALLED", values: ["INSTALLED"], count: 0 },
+  {
+    key: "UPGRADE/DOWNGRADE IN PROGRESS",
+    values: ["Upgrade/Downgrade in Progress"],
+    count: 0,
+  },
+  { key: "READY TO FINALIZE", values: ["Ready to Finalize"], count: 0 },
+];
+
 export default function Versions() {
   const [loading, setLoading] = useState(false);
+  const [options, setOptions] = useState(initialOptions);
+  const [selectedOption, setSelectedOption] = useState(options[0]);
   const [services, setServices] = useState<string[]>([]);
+  const [originalStacks, setOriginalStacks] = useState<StackVersion[]>([]);
   const [stacks, setStacks] = useState<StackVersion[]>([]);
   const { clusterName } = useContext(AppContext);
 
   const [versionModal, setVersionModal] = useState(false);
   const [installPackagesModal, setInstallPackagesModal] = useState(false);
+  const [manageVersionModal, setManageVersionsModal] = useState(false);
   const [repoModal, setRepoModal] = useState(false);
   const [hostModal, setHostModal] = useState(false);
   const [, setCompletionStatus] = useState(false);
@@ -62,12 +85,63 @@ export default function Versions() {
 
   const {} = usePolling(fetchServices, 1000);
 
+  useEffect(() => {
+    if (selectedOption.key !== "ALL") {
+      setStacks(
+        originalStacks.filter((stack) => {
+          return selectedOption.values.includes(
+            stack.ClusterStackVersions.state
+          );
+        })
+      );
+    } else {
+      setStacks(originalStacks);
+    }
+
+    const updatedOptions = options.map((option) => {
+      let count;
+      if (option.key === "ALL") {
+        count = originalStacks.length;
+      } else {
+        count = originalStacks.reduce((acc, stack) => {
+          if (option.values.includes(stack.ClusterStackVersions.state)) {
+            return acc + 1;
+          }
+          return acc;
+        }, 0);
+      }
+
+      return { ...option, count };
+    });
+
+    setOptions(updatedOptions);
+  }, [originalStacks, selectedOption]);
+
   async function fetchServices() {
     try {
-      if (!stacks) setLoading(true);
-      const response = await VersionsApi.getServices(clusterName);
+      if (originalStacks.length === 0) {
+        setLoading(true);
+      }
+      const response = await VersionsApi.getAllStacks(clusterName);
+      const stacks = response.items;
+      const currentStack = stacks.find(
+        (stack: StackVersion) => stack.ClusterStackVersions.state === "CURRENT"
+      );
+      if (currentStack) {
+        const currentRepositoryVersion =
+          currentStack.ClusterStackVersions.repository_version;
+
+        const filteredStacks = stacks.filter((stack: StackVersion) => {
+          return (
+            stack.ClusterStackVersions.repository_version >=
+            currentRepositoryVersion
+          );
+        });
+        setOriginalStacks(filteredStacks);
+      } else {
+        setOriginalStacks(stacks);
+      }
 
-      setStacks(response.items);
       const services = Object.keys(
         response.items[0].ClusterStackVersions.repository_summary.services
       );
@@ -79,6 +153,17 @@ export default function Versions() {
     }
   }
 
+  useEffect(() => {
+    fetchServices();
+  }, []);
+
+  const handleSelectChange = (selected: any) => {
+    const option = options.find((o) => o.key === selected.value);
+    if (option) {
+      setSelectedOption(option);
+    }
+  };
+
   if (loading) {
     return <Spinner />;
   }
@@ -403,11 +488,29 @@ export default function Versions() {
   return (
     <>
       <div className="mt-4">
-        <div>
-          <Button variant="success" size="sm">
-            <FontAwesomeIcon icon={faExternalLink} /> Manage versions
-          </Button>
-        </div>
+        {/* TODO: Only show Manage versions button if user has 
CLUSTER.UPGRADE_DOWNGRADE_STACK permission */}
+        <Button
+          size="sm"
+          variant="success"
+          onClick={() => setManageVersionsModal(true)}
+        >
+          <FontAwesomeIcon className="mx-2" icon={faExternalLink} /> Manage
+          versions
+        </Button>
+        <Select
+          className="ms-3"
+          value={{
+            value: selectedOption.key,
+            label: `FILTER: ${selectedOption.key} (${selectedOption.count})`,
+          }}
+          onChange={handleSelectChange}
+          options={options.map((option) => {
+            return {
+              label: `${option.key} (${option.count})`,
+              value: option.key,
+            };
+          })}
+        />
         <Table data={services} columns={columns} />
       </div>
 
@@ -462,6 +565,25 @@ export default function Versions() {
           "go to hosts";
         }}
       />
+      {manageVersionModal && (
+        <Modal
+          modalTitle="Manage Versions"
+          isOpen={manageVersionModal}
+          onClose={() => setManageVersionsModal(false)}
+          modalBody="You are about to leave the Cluster Management interface 
and go to the Ambari Administration interface. You can return to cluster 
management by using the “Go to Dashboard” link in the Ambari Administration > 
Clusters section."
+          options={{
+            cancelableViaBtn: true,
+            cancelableViaIcon: true,
+            okButtonText: "OK",
+            cancelButtonText: "CANCEL",
+          }}
+          successCallback={() => {
+            setManageVersionsModal(false);
+            // TODO: Redirect to admin view
+            // redirectToAdminView();
+          }}
+        />
+      )}
     </>
   );
-}
\ No newline at end of file
+}


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

Reply via email to