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 6d919a88b6 AMBARI-26387: Ambari Web React: List Stack in stack and
Versions (#4060)
6d919a88b6 is described below
commit 6d919a88b678982220aab6cc41cfc4df0095b1d2
Author: Sandeep Kumar <[email protected]>
AuthorDate: Tue Sep 9 20:15:03 2025 +0530
AMBARI-26387: Ambari Web React: List Stack in stack and Versions (#4060)
---
ambari-web/latest/src/api/VersionsApi.ts | 31 ++++++
ambari-web/latest/src/router/RoutesList.tsx | 9 +-
.../ClusterAdmin/StackAndVersions/ListStack.tsx | 124 +++++++++++++++++++++
.../StackAndVersions/StackAndVersions.tsx | 50 +++++++++
4 files changed, 208 insertions(+), 6 deletions(-)
diff --git a/ambari-web/latest/src/api/VersionsApi.ts
b/ambari-web/latest/src/api/VersionsApi.ts
new file mode 100644
index 0000000000..924f5ca481
--- /dev/null
+++ b/ambari-web/latest/src/api/VersionsApi.ts
@@ -0,0 +1,31 @@
+/**
+ * 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 { ambariApi } from "./config/axiosConfig";
+const VersionsApi = {
+ getServices: async function (clusterName: string) {
+ const url =
`/clusters/${clusterName}/stack_versions?fields=*,repository_versions/*`;
+ const response = await ambariApi.request({
+ url: url,
+ method: "GET",
+ });
+ return response.data;
+ },
+};
+
+export default VersionsApi;
diff --git a/ambari-web/latest/src/router/RoutesList.tsx
b/ambari-web/latest/src/router/RoutesList.tsx
index f978da8c7e..7dac38c646 100644
--- a/ambari-web/latest/src/router/RoutesList.tsx
+++ b/ambari-web/latest/src/router/RoutesList.tsx
@@ -16,6 +16,7 @@
* limitations under the License.
*/
import { RouteObject, Navigate, Outlet } from "react-router-dom";
+import StackAndVersions from
"../screens/ClusterAdmin/StackAndVersions/StackAndVersions";
function ComponentInProgress() {
return <h1>Component In Progress</h1>;
}
@@ -108,12 +109,8 @@ const RoutesList: RouteObject[] = [
element: <Outlet />,
children: [
{
- path: "stack/services",
- element: <ComponentInProgress />,
- },
- {
- path: "stack/versions",
- element: <ComponentInProgress />,
+ path: "stack/:tabName",
+ element: <StackAndVersions />,
},
{
path: "serviceAutoStart",
diff --git
a/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListStack.tsx
b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListStack.tsx
new file mode 100644
index 0000000000..ad70789415
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/ListStack.tsx
@@ -0,0 +1,124 @@
+/**
+ * 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 { useContext, useEffect, useState } from "react";
+import toast from "react-hot-toast";
+import Spinner from "../../../components/Spinner";
+import Table from "../../../components/Table";
+import { Badge, Button } from "react-bootstrap";
+import { AppContext } from "../../../store/context";
+import VersionsApi from "../../../api/VersionsApi";
+
+interface Service {
+ name: string;
+ version: string;
+ description: string;
+ status: string;
+}
+
+export default function ListStack() {
+ const [services, setServices] = useState<Service[]>([]);
+ const [loading, setLoading] = useState(false);
+ const { clusterName } = useContext(AppContext);
+
+ useEffect(() => {
+ const fetchServices = async () => {
+ try {
+ setLoading(true);
+ const response = await VersionsApi.getServices(clusterName);
+ const serviceDetails =
+ response.items[0].repository_versions[0].RepositoryVersions
+ .stack_services;
+ const serviceSummary =
+ response.items[0].ClusterStackVersions.repository_summary.services;
+ const combinedServices: Service[] = serviceDetails.map(
+ (service: any) => {
+ return {
+ name: service.display_name,
+ version: service.versions[0],
+ description: service.comment,
+ status: serviceSummary[service.name]
+ ? "Installed"
+ : "Add Service",
+ };
+ }
+ );
+
+ setServices(combinedServices);
+ setLoading(false);
+ } catch (err) {
+ toast.error("Failed to fetch data");
+ setLoading(false);
+ }
+ };
+
+ fetchServices();
+ }, []);
+
+ if (loading) {
+ return <Spinner />;
+ }
+
+ const columns = [
+ {
+ header: () => <span className="fw-bold">Service</span>,
+ accessorKey: "name",
+ width: "10%",
+ },
+ {
+ header: () => <span className="fw-bold">Version</span>,
+ accessorKey: "version",
+ width: "10%",
+ },
+ {
+ header: () => <span className="fw-bold">Status</span>,
+ accessorKey: "status",
+ width: "10%",
+ cell: (info: any) =>
+ info.getValue() === "Installed" ? (
+ <Badge bg="success">{info.getValue()}</Badge>
+ ) : (
+ <Button
+ variant="link"
+ size="sm"
+ disabled={info.getValue() === "Installed"}
+ onClick={() => {
+ //TODO: will be added once we have add service flow
+ // navigate("/main/service/add/step1");
+ }}
+ >
+ {info.getValue()}
+ </Button>
+ ),
+ },
+ {
+ header: () => <span className="fw-bold">Description</span>,
+ accessorKey: "description",
+ width: "70%",
+ },
+ ];
+
+ return (
+ <>
+ <div className="mt-4">
+ <h2>Stack</h2>
+ <Table columns={columns} data={services}></Table>
+ </div>
+ </>
+ );
+}
diff --git
a/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/StackAndVersions.tsx
b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/StackAndVersions.tsx
new file mode 100644
index 0000000000..da8156e2e7
--- /dev/null
+++
b/ambari-web/latest/src/screens/ClusterAdmin/StackAndVersions/StackAndVersions.tsx
@@ -0,0 +1,50 @@
+/**
+ * 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 { Tab, Tabs } from "react-bootstrap";
+import { useNavigate, useParams } from "react-router-dom";
+import { useState } from "react";
+import ListStack from "./ListStack";
+
+function StackAndVersions() {
+ const { tabName } = useParams();
+ const [selectedTab, setSelectedTab] = useState(tabName);
+ const navigate = useNavigate();
+
+ return (
+ <div className="py-4 mx-5">
+ <Tabs
+ className="ambari-tabs"
+ activeKey={selectedTab}
+ onSelect={(tab: any) => {
+ navigate(`/main/admin/stack/${tab}`);
+ setSelectedTab(tab);
+ }}
+ >
+ <Tab title="Stack" eventKey={"services"}>
+ <ListStack />
+ </Tab>
+ <Tab title="Versions" eventKey={"versions"}>
+ <h1>Versions.</h1>
+ </Tab>
+ </Tabs>
+ </div>
+ );
+}
+
+export default StackAndVersions;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]