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 fa26f7958a AMBARI-26370 :: Ambari Web React: Integrate Hosts tabs - 
Summary, Configs, Alerts and Versions (#4097)
fa26f7958a is described below

commit fa26f7958a83ebada9370834a98842683b7a6ac7
Author: Himanshu Maurya <[email protected]>
AuthorDate: Fri Dec 19 16:14:41 2025 +0530

    AMBARI-26370 :: Ambari Web React: Integrate Hosts tabs - Summary, Configs, 
Alerts and Versions (#4097)
---
 .../useAuth.ts => Utils/ComponentInProgress.tsx}   |  18 +-
 ambari-web/latest/src/hooks/useAuth.ts             |  10 +-
 ambari-web/latest/src/router/RoutesList.tsx        |  19 +-
 ambari-web/latest/src/screens/Hosts/details.tsx    | 938 +++++++++++++++++++++
 ambari-web/latest/src/screens/Hosts/index.tsx      | 513 +++++++++++
 .../screens/Hosts/supportClientConfigsDownload.ts  |  73 ++
 6 files changed, 1550 insertions(+), 21 deletions(-)

diff --git a/ambari-web/latest/src/hooks/useAuth.ts 
b/ambari-web/latest/src/Utils/ComponentInProgress.tsx
similarity index 66%
copy from ambari-web/latest/src/hooks/useAuth.ts
copy to ambari-web/latest/src/Utils/ComponentInProgress.tsx
index c7d39f2554..4034b30d84 100644
--- a/ambari-web/latest/src/hooks/useAuth.ts
+++ b/ambari-web/latest/src/Utils/ComponentInProgress.tsx
@@ -16,18 +16,6 @@
  * limitations under the License.
  */
 
-//TODO: This will be implemented soon to fetch auth info
-
-export const useAuth = () => {
-  const hasAuthorization = (authId: string) => {
-    return authId ? true : false; //TODO: will be implemented soon
-  };
-
-  const havePermissions = (authRoles: string) => {
-    return authRoles ? true : false; //TODO: will be implemented soon
-  };
-
-  return { hasAuthorization, havePermissions };
-}
-
-export default useAuth;
+export function ComponentInProgress() {
+    return <h1>Component In Progress</h1>;
+}
\ No newline at end of file
diff --git a/ambari-web/latest/src/hooks/useAuth.ts 
b/ambari-web/latest/src/hooks/useAuth.ts
index c7d39f2554..56684f6c9c 100644
--- a/ambari-web/latest/src/hooks/useAuth.ts
+++ b/ambari-web/latest/src/hooks/useAuth.ts
@@ -27,7 +27,15 @@ export const useAuth = () => {
     return authRoles ? true : false; //TODO: will be implemented soon
   };
 
-  return { hasAuthorization, havePermissions };
+  const isAdmin = () => {
+    return true; //TODO: will be implemented soon
+  }
+
+  const isOperator = () => {
+    return true; //TODO: will be implemented soon
+  }
+
+  return { hasAuthorization, havePermissions, isAdmin, isOperator };
 }
 
 export default useAuth;
diff --git a/ambari-web/latest/src/router/RoutesList.tsx 
b/ambari-web/latest/src/router/RoutesList.tsx
index 7dac38c646..680b436b1d 100644
--- a/ambari-web/latest/src/router/RoutesList.tsx
+++ b/ambari-web/latest/src/router/RoutesList.tsx
@@ -15,11 +15,12 @@
  * See the License for the specific language governing permissions and
  * 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>;
-}
+import { ComponentInProgress } from "../Utils/ComponentInProgress";
+import HostsList from "../screens/Hosts/HostsList";
+import { Hosts } from "../screens/Hosts";
 
 const RoutesList: RouteObject[] = [
   {
@@ -86,11 +87,19 @@ const RoutesList: RouteObject[] = [
           },
           {
             path: "hosts",
-            element: <ComponentInProgress />,
+            element: <HostsList />,
+          },
+          {
+            path: "hosts/component/:componentName",
+            element: <HostsList />,
+          },
+          {
+            path: "hosts/version/:versionName/:versionStatus",
+            element: <HostsList />,
           },
           {
             path: "hosts/:hostname/:tab",
-            element: <ComponentInProgress />,
+            element: <Hosts />,
           },
           {
             path: "host/add/:stepNumber",
diff --git a/ambari-web/latest/src/screens/Hosts/details.tsx 
b/ambari-web/latest/src/screens/Hosts/details.tsx
new file mode 100644
index 0000000000..2d2a9d73ba
--- /dev/null
+++ b/ambari-web/latest/src/screens/Hosts/details.tsx
@@ -0,0 +1,938 @@
+/**
+ * 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 { get, set } from "lodash";
+import {
+  addDeleteComponentsMap,
+  getComponentDisplayName,
+  getComponentName,
+  getHostComponentsInfo,
+  serviceNonClientActiveComponents,
+  setRackInfo,
+  showConfirmationPopup,
+} from "./utils";
+import modalManager from "../../store/ModalManager";
+import { HostsApi } from "../../api/hostsApi";
+import {
+  showAlertModal,
+  showErrorModal,
+  translate,
+  translateWithVariables,
+} from "../../Utils/Utility";
+import {
+  defaultSuccessCallback,
+  infoPassiveState,
+  restartHostComponents,
+} from "./batchUtils";
+import { IHostStackVersion } from "../../models/hostStackVersion";
+import { Alert } from "react-bootstrap";
+import { IHostComponent } from "../../models/hostComponent";
+import { ComponentStatus } from "./enums";
+import { checkNnLastCheckpointTime } from "./actions";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faWarning } from "@fortawesome/free-solid-svg-icons";
+import { IHost } from "../../models/host";
+import {
+  downloadClientConfigsCall,
+  ResourceTypeEnum,
+} from "./supportClientConfigsDownload";
+//TODO: uncomment below lines and their usages after these components are 
available
+// import BackgroundOperations from "../BackgroundOperations";
+// import RecommendationModal from "../../components/RecommendationModal";
+
+export const doAction = (option: any) => {
+  switch (option.action) {
+    case "deleteHost":
+      validateAndDeleteHost(option);
+      break;
+    case "startAllComponents":
+      doStartAllComponents(option);
+      break;
+    case "stopAllComponents":
+      doStopAllComponents(option);
+      break;
+    case "restartAllComponents":
+      doRestartAllComponents(option);
+      break;
+    case "onOffPassiveModeForHost":
+      onOffPassiveModeForHost(option);
+      break;
+    case "setRackId":
+      setRackIdForHost(option);
+      break;
+    case "downloadClientConfigs":
+      downloadClientConfigs(option);
+      break;
+    case "downloadAllClientConfigs":
+      downloadAllClientConfigs(option);
+      break;
+    case "regenerateKeytabFileOperations":
+      regenerateKeytabFileOperations(option);
+      break;
+    default:
+      break;
+  }
+};
+
+const setRackIdForHost = (option: any) => {
+  const operationData = {
+    message: translate("hosts.host.details.setRackId"),
+  };
+  setRackInfo(
+    operationData,
+    [
+      {
+        hostName: option.hostName,
+      },
+    ],
+    option.clusterName,
+    option.callback,
+    option.rack
+  );
+};
+
+const onOffPassiveModeForHost = (context: any) => {
+  const state = context.active ? "ON" : "OFF";
+  const message = translateWithVariables("hosts.host.details.for.postfix", {
+    "0": context.label,
+  }) as string;
+  let popupInfo = translateWithVariables("hosts.passiveMode.popup", {
+    "0": context.active ? "On" : "Off",
+    "1": context.hostName,
+  });
+
+  let popupAlert = "";
+
+  if (state === "OFF") {
+    const currentHostVersion = get(context, "host.stackVersions", []).find(
+      (version: IHostStackVersion) => version.isCurrent()
+    )?.repoVersion;
+    const currentClusterVersion = get(context, "clusterStackVersion", []).find(
+      (version: any) => version.state === "CURRENT"
+    )?.repository_version;
+    if (currentHostVersion !== currentClusterVersion) {
+      popupAlert = translateWithVariables(
+        "hosts.passiveMode.popup.version.mismatch",
+        {
+          "0": context.hostName,
+          "1": currentClusterVersion,
+        }
+      ) as string;
+    }
+  }
+  let modalProps = {
+    isOpen: true,
+    onClose: () => {},
+    modalTitle: translate("popup.confirmation.commonHeader"),
+    modalBody: (
+      <div>
+        <div>{popupInfo}</div>
+        {popupAlert ? (
+          <Alert variant="warning" className="mt-2">
+            {popupAlert}
+          </Alert>
+        ) : null}
+      </div>
+    ),
+    successCallback: () => {
+      hostPassiveModeRequest(
+        state,
+        message,
+        context.hostName,
+        context.clusterName
+      );
+      modalManager.hide();
+    },
+    options: {
+      buttonSize: "sm" as "sm" | "lg" | undefined,
+      cancelableViaIcon: true,
+      cancelableViaBtn: true,
+      okButtonVariant: "primary",
+    },
+  };
+  modalManager.show(modalProps);
+};
+
+const hostPassiveModeRequest = async (
+  state: string,
+  message: string,
+  hostNames: string,
+  clusterName: string
+) => {
+  const data = {
+    RequestInfo: {
+      context: message,
+      query: "Hosts/host_name.in(" + hostNames + ")",
+    },
+    Body: {
+      Hosts: {
+        maintenance_state: state,
+      },
+    },
+  };
+
+  try {
+    await HostsApi.updateHost(clusterName, data);
+    updateHost(state);
+  } catch (error) {
+    showErrorModal(message + get(error, "message", ""));
+  }
+};
+
+const updateHost = (state: string) => {
+  infoPassiveState(state);
+  // setTimeout(() => {
+  //   window.location.reload();
+  // }, 2000);
+};
+
+const doStartAllComponents = (context: any) => {
+  const hostComponents: IHostComponent[] = get(
+    context,
+    "host.hostComponents",
+    []
+  );
+  const components = serviceNonClientActiveComponents(hostComponents);
+  if (components.length) {
+    showConfirmationPopup(translate("question.sure.startAll") as string, () =>
+      sendComponentsCommand(
+        components,
+        translate(
+          "hosts.host.maintainance.startAllComponents.context"
+        ) as string,
+        ComponentStatus.STARTED
+      )
+    );
+  }
+};
+
+export const sendComponentsCommand = async (
+  component: IHostComponent[],
+  context: string,
+  state: string
+) => {
+  const clusterName = get(component, "[0].clusterName", "");
+  const hostName = get(component, "[0].hostName", "");
+  const query =
+    "HostRoles/component_name.in(" +
+    component.map(getComponentName).join(",") +
+    ")";
+  const data = {
+    RequestInfo: {
+      context: context,
+      operation_level: {
+        level: "HOST",
+        cluster_name: clusterName,
+        host_name: hostName,
+      },
+      query: query,
+    },
+    Body: {
+      HostRoles: {
+        state: state,
+      },
+    },
+  };
+
+  const response = await HostsApi.updateHostComponentsForHost(
+    clusterName,
+    hostName,
+    query,
+    data
+  );
+  const requestId = get(response, "Requests.id", -1);
+  if (requestId !== -1) {
+    // modalManager.show(
+    //   <BackgroundOperations
+    //     isOpen={true}
+    //     onClose={() => {
+    //       modalManager.hide();
+    //     }}
+    //     requestId={requestId}
+    //   />
+    // );
+  }
+};
+
+const doStopAllComponents = (context: any) => {
+  const hostComponents: IHostComponent[] = get(
+    context,
+    "host.hostComponents",
+    []
+  );
+  const hostName = context.hostName;
+  const clusterName = get(context, "clusterName", "");
+  const components: IHostComponent[] =
+    serviceNonClientActiveComponents(hostComponents);
+  if (components.length) {
+    if (
+      components.find(
+        (component: IHostComponent) =>
+          getComponentName(component) === "NAMENODE"
+      )?.workStatus === ComponentStatus.STARTED
+    ) {
+      checkNnLastCheckpointTime(
+        () =>
+          showConfirmationPopup(
+            translate("question.sure.stopAll") as string,
+            () =>
+              sendComponentsCommand(
+                components,
+                translate(
+                  "hosts.host.maintainance.stopAllComponents.context"
+                ) as string,
+                ComponentStatus.STOPPED
+              )
+          ),
+        hostName,
+        clusterName
+      );
+    } else {
+      showConfirmationPopup(translate("question.sure.stopAll") as string, () =>
+        sendComponentsCommand(
+          components,
+          translate(
+            "hosts.host.maintainance.stopAllComponents.context"
+          ) as string,
+          ComponentStatus.STOPPED
+        )
+      );
+    }
+  }
+};
+
+const doRestartAllComponents = (context: any) => {
+  const hostComponents: IHostComponent[] = get(
+    context,
+    "host.hostComponents",
+    []
+  );
+  const hostName = context.hostName;
+  const clusterName = get(context, "clusterName", "");
+  const components: IHostComponent[] =
+    serviceNonClientActiveComponents(hostComponents);
+  if (components.length) {
+    if (
+      components.find(
+        (component: IHostComponent) =>
+          getComponentName(component) === "NAMENODE"
+      )?.workStatus === ComponentStatus.STARTED
+    ) {
+      checkNnLastCheckpointTime(
+        () =>
+          showConfirmationPopup(
+            translate("question.sure.restartAll") as string,
+            () =>
+              restartHostComponents(
+                components,
+                translateWithVariables(
+                  "rollingrestart.context.allOnSelectedHost",
+                  {
+                    "0": hostName,
+                  }
+                ) as string,
+                "HOST"
+              )
+          ),
+        hostName,
+        clusterName
+      );
+    } else {
+      showConfirmationPopup(
+        translate("question.sure.restartAll") as string,
+        () =>
+          restartHostComponents(
+            components,
+            translateWithVariables("rollingrestart.context.allOnSelectedHost", 
{
+              "0": hostName,
+            }) as string,
+            "HOST"
+          )
+      );
+    }
+  }
+};
+
+const downloadClientConfigs = (context: any) => {
+  const data = {
+    clusterName: context.clusterName,
+    hostName: context.hostName,
+    componentName: context.componentName,
+    resourceType: ResourceTypeEnum.HOST_COMPONENT,
+  };
+  downloadClientConfigsCall(data);
+};
+
+const downloadAllClientConfigs = (context: any) => {
+  const data = {
+    clusterName: context.clusterName,
+    hostName: context.hostName,
+    resourceType: ResourceTypeEnum.HOST,
+  };
+  downloadClientConfigsCall(data);
+};
+
+export const confirmRecoverHost = (context: any) => {
+  const host: IHost = context.host;
+  const hostComponents: IHostComponent[] = get(host, "hostComponents", []);
+  const allowedStates = [
+    ComponentStatus.STOPPED,
+    ComponentStatus.INSTALL_FAILED,
+    ComponentStatus.INIT,
+  ];
+  let componentsNotStopped: IHostComponent[] = [];
+  hostComponents.forEach((component: IHostComponent) => {
+    if (!allowedStates.includes(component.workStatus as ComponentStatus)) {
+      componentsNotStopped.push(component);
+    }
+  });
+  if (componentsNotStopped.length) {
+    let body = translate("hosts.recover.error.popup.body") as string;
+    if (body.includes("{0}")) {
+      body = body.replace(
+        "{0}",
+        componentsNotStopped
+          .map((component: IHostComponent) => getComponentName(component))
+          .join(", ")
+      );
+    }
+    const modalProps = {
+      isOpen: true,
+      onClose: () => {},
+      modalTitle: translate("hosts.recover.error.popup.title"),
+      modalBody: (
+        <Alert variant="warning">
+          <FontAwesomeIcon icon={faWarning} className="text-warning" />
+          {body}
+        </Alert>
+      ),
+      successCallback: () => {
+        modalManager.hide();
+      },
+      options: {
+        buttonSize: "sm" as "sm" | "lg" | undefined,
+        cancelableViaIcon: true,
+        cancelableViaBtn: false,
+        okButtonVariant: "primary",
+      },
+    };
+    modalManager.show(modalProps);
+  } else {
+    const modalProps = {
+      isOpen: true,
+      onClose: () => {},
+      modalTitle: translate("hosts.recover.popup.title"),
+      modalBody: translate("hosts.recover.popup.body"),
+      successCallback: () => {
+        recoverHost(context);
+        modalManager.hide();
+      },
+      options: {
+        buttonSize: "sm" as "sm" | "lg" | undefined,
+        cancelableViaIcon: true,
+        cancelableViaBtn: true,
+        okButtonVariant: "primary",
+        okButtonText: "YES",
+        cancelButtonText: "NO",
+      },
+    };
+    modalManager.show(modalProps);
+  }
+};
+
+const recoverHost = async (context: any) => {
+  const host: IHost = context.host;
+  const components = host.hostComponents;
+  const hostName = host.hostName;
+  const clusterName = get(context, "clusterName", "");
+  const isKerberosEnabled = get(context, "isKerberosEnabled", false);
+  const batches: any[] = [
+    {
+      order_id: 1,
+      type: "PUT",
+      uri: `/clusters/${clusterName}/hosts/${hostName}/host_components`,
+      RequestBodyInfo: {
+        RequestInfo: {
+          context: translate("hosts.host.recover.initAllComponents.context"),
+          operation_level: {
+            level: "HOST",
+            cluster_name: clusterName,
+            host_name: hostName,
+          },
+          query: `HostRoles/component_name.in(${components
+            .map((c) => c.componentName)
+            .join(",")})`,
+        },
+        Body: {
+          HostRoles: {
+            state: "INIT",
+          },
+        },
+      },
+    },
+  ];
+
+  batches.push({
+    order_id: 2,
+    type: "PUT",
+    uri: `/clusters/${clusterName}/hosts/${hostName}/host_components`,
+    RequestBodyInfo: {
+      RequestInfo: {
+        context: translate("hosts.host.recover.installAllComponents.context"),
+        operation_level: {
+          level: "HOST",
+          cluster_name: clusterName,
+          host_name: hostName,
+        },
+        query: `HostRoles/component_name.in(${components
+          .map((c) => c.componentName)
+          .join(",")})`,
+      },
+      Body: {
+        HostRoles: {
+          state: "INSTALLED",
+        },
+      },
+    },
+  });
+
+  if (isKerberosEnabled) {
+    batches.push({
+      order_id: 3,
+      type: "PUT",
+      uri: `/clusters/${clusterName}`,
+      RequestBodyInfo: {
+        RequestInfo: {
+          context: translate("hosts.host.recover.regenerateKeytabs.context"),
+          query: 
`regenerate_keytabs=all&regenerate_hosts=${hostName}&config_update_policy=none`,
+        },
+        Body: {
+          Clusters: {
+            security_type: "KERBEROS",
+          },
+        },
+      },
+    });
+  }
+
+  await context.getKDCSessionState(() => {
+    doRecoverHost(batches, clusterName);
+  });
+};
+
+const doRecoverHost = async (batches: any[], clusterName: string) => {
+  const data = {
+    intervalTimeSeconds: 1,
+    tolerateSize: 0,
+    batches: batches,
+  };
+  try {
+    const respone = await HostsApi.batchRequest(clusterName, data);
+    recoverHostSuccessCallback(respone);
+  } catch (error) {
+    showErrorModal(get(error, "message", ""));
+  }
+};
+
+const recoverHostSuccessCallback = (response: any) => {
+  const requestId = get(response, "data.resources.[0].RequestSchedule.id", -1);
+  if (requestId !== -1) {
+    // modalManager.show(
+    //   <BackgroundOperations
+    //     isOpen={true}
+    //     onClose={() => {
+    //       modalManager.hide();
+    //     }}
+    //     requestId={requestId}
+    //   />
+    // );
+  }
+};
+
+const validateAndDeleteHost = (context: any) => {
+  const clusterComponents = get(context, "clusterComponents", []);
+  const serviceModels = get(context, "serviceModels", {});
+  const container = getHostComponentsInfo(
+    get(context, "host.hostComponents", []),
+    clusterComponents,
+    serviceModels
+  );
+  const properties = {};
+  const hostData = get(context, "host", {} as IHost);
+  if (container.nonDeletableComponents.length > 0) {
+    raiseDeleteComponentsError(container, "nonDeletableList", hostData);
+    return;
+  } else if (container.nonAddableMasterComponents.length > 0) {
+    raiseDeleteComponentsError(container, "masterList", hostData);
+    return;
+  } else if (container.runningComponents.length > 0) {
+    raiseDeleteComponentsError(container, "runningList", hostData);
+    return;
+  } else if (container.lastMasterComponents.length > 0) {
+    raiseDeleteComponentsError(container, "lastMasterList", hostData);
+    return;
+  }
+
+  set(properties, "fromDeleteHost", true);
+
+  if (container.isReconfigureRequired) {
+    reconfigureAndDeleteHost(container, context, properties);
+  } else {
+    confirmDeleteHost(container, context);
+  }
+};
+
+const raiseDeleteComponentsError = (
+  container: any,
+  type: string,
+  hostData: IHost
+) => {
+  let components = [];
+  if (type === "nonDeletableList") {
+    components = container.nonDeletableComponents;
+  } else if (type === "masterList") {
+    components = container.nonAddableMasterComponents;
+  } else if (type === "runningList") {
+    components = container.runningComponents;
+  } else if (type === "lastMasterList") {
+    components = container.lastMasterComponents;
+  }
+
+  let componentsBody = translate(`hosts.cant.do.popup.${type}.body`) as string;
+  if (componentsBody.includes("{0}")) {
+    componentsBody = componentsBody.replace("{0}", components.length);
+  }
+
+  const hostComponents = hostData.hostComponents.filter(
+    (component: IHostComponent) =>
+      components.includes(getComponentDisplayName(component))
+  );
+  const decommissionableComponents = hostComponents.filter(
+    (component: IHostComponent) => component.decommissionAllowed
+  );
+
+  const showBodyEnd = ["runningList", "masterList", "lastMasterList"].includes(
+    type
+  );
+  let componentsBodyEnd = "";
+  if (showBodyEnd) {
+    componentsBodyEnd = translate(
+      `hosts.cant.do.popup.${type}.body.end`
+    ) as string;
+    if (componentsBodyEnd.includes("{0}")) {
+      componentsBodyEnd = componentsBodyEnd.replace(
+        "{0}",
+        decommissionableComponents.map(getComponentDisplayName).join(", ")
+      );
+    }
+  }
+
+  const modalProps = {
+    isOpen: true,
+    onClose: () => {},
+    modalTitle: translate("hosts.cant.do.popup.title"),
+    modalBody: (
+      <div>
+        <Alert variant="warning" className="mt-2">
+          <div className="d-flex">
+            <div className="me-4">
+              <FontAwesomeIcon icon={faWarning} className="text-warn" />
+            </div>
+            <div>
+              <div className="text-dark fw-bold mb-2">{componentsBody}</div>
+              <div>{components.join(", ")}</div>
+              {showBodyEnd ? (
+                <div className="mt-4">{componentsBodyEnd}</div>
+              ) : null}
+            </div>
+          </div>
+        </Alert>
+      </div>
+    ),
+    successCallback: () => {
+      modalManager.hide();
+    },
+    options: {
+      buttonSize: "sm" as "sm" | "lg" | undefined,
+      cancelableViaIcon: true,
+      cancelableViaBtn: false,
+      okButtonVariant: "primary",
+    },
+  };
+
+  modalManager.show(modalProps);
+};
+
+const reconfigureAndDeleteHost = (
+  _container: any,
+  context: any,
+  properties: any
+) => {
+  const hostName = get(context, "hostName");
+  let reconfiguredComponents: any = [];
+  const loadComponentRelatedConfigs = get(
+    context,
+    "loadComponentRelatedConfigs"
+  );
+
+  get(context, "host.hostComponents", []).forEach(
+    (component: IHostComponent) => {
+      const componentsMapItem = 
addDeleteComponentsMap[component.componentName];
+      if (componentsMapItem) {
+        reconfiguredComponents.push(component.displayName);
+        if (componentsMapItem.hostPropertyName) {
+          set(properties, componentsMapItem.hostPropertyName, hostName);
+        }
+        if (componentsMapItem.addPropertyName) {
+          set(properties, componentsMapItem.addPropertyName, true);
+        }
+        loadComponentRelatedConfigs(
+          componentsMapItem.configTagsCallbackName,
+          componentsMapItem.configsCallbackName,
+          properties
+        );
+      }
+    }
+  );
+
+  // modalManager.show(
+  //   <RecommendationModal
+  //     isOpen={true}
+  //     onClose={() => {
+  //       modalManager.hide();
+  //     }}
+  //     componentDisplayName={reconfiguredComponents.join(", ")}
+  //     add={false}
+  //     callback={() => confirmDeleteHost(container, context)}
+  //     commonMessage={
+  //       translateWithVariables(
+  //         "hosts.host.delete.componentsRequireReconfigure",
+  //         {
+  //           "0": reconfiguredComponents.join(", "),
+  //         }
+  //       ) as string
+  //     }
+  //   />
+  // );
+};
+
+const confirmDeleteHost = (container: any, context: any) => {
+  const publicHostName = get(context, "host.publicHostName", "");
+
+  const header = translate("hosts.delete.popup.title");
+  const deletePopupBody = translateWithVariables("hosts.delete.popup.body", {
+    "0": publicHostName,
+  });
+
+  let lastComponentError = "";
+  if (container.lastComponents.length > 0) {
+    lastComponentError = translateWithVariables(
+      "hosts.delete.popup.body.msg4",
+      {
+        "0": container.lastComponents.join(", "),
+      }
+    ) as string;
+  }
+
+  let unknownComponents = "";
+  if (container.unknownComponents.length > 0) {
+    unknownComponents = container.unknownComponents.join(", ");
+  }
+
+  let decommissionWarning = "";
+  if (container.toDecommissionComponents.length > 0) {
+    decommissionWarning = translateWithVariables(
+      "hosts.delete.popup.body.msg7",
+      {
+        "0": container.toDecommissionComponents.join(", "),
+      }
+    ) as string;
+  }
+
+  const modalProps = {
+    isOpen: true,
+    onClose: () => {},
+    modalTitle: header,
+    modalBody: (
+      <div>
+        {unknownComponents ? (
+          <div>
+            <div>{translate("hosts.delete.popup.unknownComponents")}</div>
+            <div className="mt-1">{unknownComponents}</div>
+          </div>
+        ) : null}
+        <div>
+          <FontAwesomeIcon icon={faWarning} className="text-warning me-2" />
+          {deletePopupBody}
+        </div>
+        {lastComponentError ? (
+          <div className="mt-2">
+            <Alert variant="warning" className="mt-2">
+              {lastComponentError}
+            </Alert>
+          </div>
+        ) : null}
+        {decommissionWarning ? (
+          <div className="mt-2">
+            <Alert variant="warning" className="mt-2">
+              {decommissionWarning}
+            </Alert>
+          </div>
+        ) : null}
+        <Alert variant="warning" className="mt-2">
+          <div>{translate("common.important.strong")}</div>
+          {unknownComponents ? (
+            <div className="mt-2">
+              {translate("hosts.delete.popup.body.msg.unknownComponents")}
+            </div>
+          ) : null}
+          <div>{translate("hosts.delete.popup.body.msg1")}</div>
+        </Alert>
+        {!unknownComponents ? (
+          <Alert variant="warning" className="mt-2">
+            <span>{translate("hosts.delete.popup.body.msg5")}</span>
+            <span className="text-danger">
+              {translate("hosts.delete.popup.body.msg6")}
+            </span>
+          </Alert>
+        ) : null}
+        <Alert variant="warning" className="mt-2">
+          <div>{translate("common.important.strong")}</div>
+          <div>{translate("hosts.delete.popup.body.msg3")}</div>
+        </Alert>
+      </div>
+    ),
+    successCallback: () => {
+      doDeleteHost(context, container, {}, {});
+      modalManager.hide();
+    },
+    options: {
+      buttonSize: "sm" as "sm" | "lg" | undefined,
+      cancelableViaIcon: true,
+      cancelableViaBtn: true,
+      okButtonVariant: "primary",
+    },
+  };
+
+  modalManager.show(modalProps);
+};
+
+const doDeleteHost = (
+  context: any,
+  container: any,
+  groupedPropertiesToChange: any,
+  deletedHostComponentError: any
+) => {
+  const allComponents = get(context, "host.hostComponents", []);
+  const doDeleteHostComponent = get(context, "doDeleteHostComponent");
+  const applyConfigsCustomization = get(
+    context,
+    "applyConfigsCustomization",
+    () => {}
+  );
+  const putConfigsToServer = get(context, "putConfigsToServer", () => {});
+  const clearConfigsChanges = get(context, "clearConfigsChanges", () => {});
+  let deleteRequests = [];
+
+  const deleteHost = async () => {
+    if (allComponents.length > 0) {
+      for (const component of allComponents) {
+        deleteRequests.push(doDeleteHostComponent(component));
+      }
+      try {
+        await Promise.all(deleteRequests);
+        if (container.isReconfigureRequired) {
+          const reconfiguredComponents = allComponents
+            .filter(
+              (component: IHostComponent) =>
+                addDeleteComponentsMap[component.componentName]
+            )
+            .map((component: any) => component.displayName)
+            .join(", ");
+          applyConfigsCustomization();
+          putConfigsToServer(groupedPropertiesToChange, 
reconfiguredComponents);
+          clearConfigsChanges();
+        }
+        await deleteHostCall(context);
+      } catch (e) {
+        set(
+          deletedHostComponentError,
+          "xhr.responseText",
+          `{"message": "${get(
+            deletedHostComponentError,
+            "xhr.statusText",
+            ""
+          )}"}`
+        );
+        showErrorModal(get(deletedHostComponentError, "xhr.responseText", ""));
+      }
+    }
+  };
+
+  return deleteHost();
+};
+
+const deleteHostCall = async (context: any) => {
+  const clusterName = get(context, "host.cluster", "");
+  const hostName = get(context, "host.hostName", "");
+  try {
+    await HostsApi.deleteHost(clusterName, hostName);
+    deleteHostCallSuccessCallback();
+  } catch (error) {
+    deleteHostCallErrorCallback(error);
+  }
+};
+
+const deleteHostCallSuccessCallback = () => {
+  window.location.href = "#/main/hosts";
+};
+
+const deleteHostCallErrorCallback = (error: any) => {
+  const errorMessage = get(error, "message", "");
+  showErrorModal(errorMessage);
+};
+
+const regenerateKeytabFileOperations = (context: any) => {
+  const clusterName = get(context, "clusterName", "");
+  const hostName = get(context, "hostName", "");
+  showConfirmationPopup(
+    translateWithVariables("question.sure.regenerateKeytab.host", {
+      "0": hostName,
+    }) as string,
+    async () => {
+      try {
+        const response = await HostsApi.regenerateKeytabsForHost(
+          clusterName,
+          hostName
+        );
+        const requestId = get(response, "Requests.id", -1);
+        defaultSuccessCallback(requestId);
+      } catch (error) {
+        showAlertModal(
+          translate("common.error"),
+          translateWithVariables(
+            "alerts.notifications.regenerateKeytab.host.error",
+            {
+              "0": hostName,
+            }
+          )
+        );
+      }
+    }
+  );
+};
diff --git a/ambari-web/latest/src/screens/Hosts/index.tsx 
b/ambari-web/latest/src/screens/Hosts/index.tsx
new file mode 100644
index 0000000000..37a072e4e8
--- /dev/null
+++ b/ambari-web/latest/src/screens/Hosts/index.tsx
@@ -0,0 +1,513 @@
+/**
+ * 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.
+ */
+
+//TODO: uncomment the commented imports and their usages after those 
components are available
+
+import { Button, Col, Nav, Row, Tab } from "react-bootstrap";
+import HostsSummary from "./HostSummary";
+// import HostConfigs from "./HostConfigs";
+// import HostAlerts from "../Alerts/HostAlerts";
+// import HostStackVersions from "../Alerts/HostStackVersions";
+import NestedDropdown from "../../components/NestedDropdown";
+import { forEach, get } from "lodash";
+import { useContext, useEffect, useMemo, useState } from "react";
+import IHost from "../../models/host";
+import { useHostConfigUpdater } from "../../hooks/useHostConfigUpdater";
+import { useParams, useNavigate } from "react-router-dom";
+import { IHostComponent } from "../../models/hostComponent";
+import { AppContext } from "../../store/context";
+import { supports } from "../../data/configs/services/config";
+import { confirmRecoverHost, doAction } from "./details";
+import { useHostChecks } from "../../hooks/useHostChecks";
+import HostChecks from "./HostChecks";
+import modalManager from "../../store/ModalManager";
+import useStackVersion from "../../hooks/useStackVersion";
+import { HostsApi } from "../../api/hostsApi";
+import Spinner from "../../components/Spinner";
+// import { ServiceContext } from "../../store/ServiceContext";
+import useKDCSessionState from "../../hooks/useKDCSessionState";
+// import useStackServices from "../../hooks/useStackServices";
+// import { useConfigs } from "../../hooks/useConfigs";
+// import useComponentAddDelete from "./hooks/useComponentAddDelete";
+import { translate, translateWithVariables } from "../../Utils/Utility";
+import classNames from "classnames";
+import { useAuth } from "../../hooks/useAuth";
+import { ComponentInProgress } from "../../Utils/ComponentInProgress";
+
+export function Hosts() {
+  const params = useParams();
+  const navigate = useNavigate();
+  const { isKerberosEnabled, clusterName, upgradeIsRunning } =
+    useContext(AppContext);
+  // const { allServiceModels: serviceModels } = useContext(ServiceContext);
+  const [allHostModels, setAllHostModels] = useState<IHost[]>([]);
+  const { stackVersionList } = useStackVersion();
+  const [showHostCheck, setShowHostCheck] = useState(false);
+  const [clusterComponents, setClusterComponents] = useState<any>({});
+  const [loading, setLoading] = useState(true);
+
+  // const { services: stackServices } = useStackServices();
+  // const { getConfigByName } = useConfigs([], stackServices as any);
+  const { getKDCSessionState } = useKDCSessionState(() => {});
+  const [activeTab, setActiveTab] = useState(params.tab || "summary");
+
+  // Authorization hooks - implementing Ember.js host authorization patterns
+  const { hasAuthorization, isAdmin, isOperator, havePermissions } = useAuth();
+
+  // Use computed upgrade property instead of utility function
+  const isUpgradeInProgress = upgradeIsRunning;
+
+  // Check specific authorizations for host operations
+  const canStartStopServices = hasAuthorization("SERVICE.START_STOP");
+  const canToggleHostMaintenance = hasAuthorization("HOST.TOGGLE_MAINTENANCE");
+  const canAddDeleteHosts = hasAuthorization("HOST.ADD_DELETE_HOSTS");
+  const canViewConfigs =
+    hasAuthorization("SERVICE.VIEW_CONFIGS") ||
+    hasAuthorization("CLUSTER.VIEW_CONFIGS");
+  const canRunHostChecks = isAdmin() || isOperator();
+
+  // Overall permission check for Host Actions menu - matches Ember template 
logic
+  // {{#havePermissions "HOST.ADD_DELETE_COMPONENTS, HOST.TOGGLE_MAINTENANCE, 
HOST.ADD_DELETE_HOSTS"}}
+  const canShowHostActions = havePermissions("HOST.ADD_DELETE_COMPONENTS, 
HOST.TOGGLE_MAINTENANCE, HOST.ADD_DELETE_HOSTS");
+
+  const hostApiQueryParams = useMemo(() => {
+    return {
+      RequestInfo: {
+        query: `Hosts/host_name.in(${params.hostname})`,
+      },
+    };
+  }, []);
+
+  useHostConfigUpdater(hostApiQueryParams, allHostModels, setAllHostModels);
+  const { startHostCheck, stopHostCheck, isHostCheckRunning, hostCheckResult } 
=
+    useHostChecks();
+
+  // const {
+  //   _doDeleteHostComponent,
+  //   applyConfigsCustomization,
+  //   putConfigsToServer,
+  //   clearConfigsChanges,
+  //   loadComponentRelatedConfigs,
+  // } = useComponentAddDelete(
+  //   clusterComponents,
+  //   stackServices,
+  //   getConfigByName,
+  //   setAllHostModels
+  // );
+
+  const getBootHostsProp = () => {
+    let bootHosts = [];
+    const hostName = get(allHostModels, "[0].hostName", "");
+    const host = {
+      name: hostName,
+    };
+    bootHosts.push(host);
+    return bootHosts;
+  };
+
+  useEffect(() => {
+    if (params.tab && params.tab !== activeTab) {
+      setActiveTab(params.tab);
+    }
+  }, [params.tab]);
+
+  useEffect(() => {
+    getClusterComponents();
+  }, [get(allHostModels, "[0].hostComponents", []).length]);
+
+  useEffect(() => {
+    if (showHostCheck) {
+      startHostCheck(getBootHostsProp());
+    } else {
+      stopHostCheck();
+    }
+  }, [showHostCheck]);
+
+  const getAlerts = () => {
+    return {
+      critical: get(allHostModels, "[0].alertsSummary.CRITICAL", 0),
+      warning: get(allHostModels, "[0].alertsSummary.WARNING", 0),
+    };
+  };
+
+  const tabs = {
+    summary: {
+      title: "SUMMARY",
+      component: (
+        <HostsSummary
+          allHostModels={allHostModels}
+          setAllHostModels={setAllHostModels}
+          clusterComponents={clusterComponents}
+        />
+      ),
+    },
+    configs: {
+      title: "CONFIGS",
+      component: <ComponentInProgress />,
+      // component: <HostConfigs />,
+    },
+    alerts: {
+      title: (
+        <>
+          ALERTS{" "}
+          <Button
+            className={classNames("me-1 text-white fs-10 rounded-1 px-1 py-0", 
{
+              "bg-danger": getAlerts().critical > 0,
+              "bg-orange":
+                getAlerts().warning > 0 && getAlerts().critical === 0,
+              "bg-light":
+                getAlerts().critical === 0 && getAlerts().warning === 0,
+            })}
+          >
+            {getAlerts().critical + getAlerts().warning}
+          </Button>
+        </>
+      ),
+      component: <ComponentInProgress />,
+      // component: <HostAlerts hostname={params.hostname} />,
+    },
+    versions: {
+      title: "VERSIONS",
+      component: <ComponentInProgress />,
+      // component: <HostStackVersions />,
+    },
+  };
+
+  const isActive = () => {
+    return get(allHostModels, "[0]")?.isActive();
+  };
+
+  const getClusterComponents = async () => {
+    setLoading(true);
+    const response = await HostsApi.getClusterComponents(
+      clusterName,
+      
"ServiceComponentInfo/service_name,host_components/HostRoles/display_name,host_components/HostRoles/host_name,host_components/HostRoles/public_host_name,host_components/HostRoles/state,host_components/HostRoles/maintenance_state,host_components/HostRoles/stale_configs,host_components/HostRoles/ha_state,host_components/HostRoles/desired_admin_state,&minimal_response=true"
+    );
+    setClusterComponents(response);
+    setLoading(false);
+  };
+
+  const getMaintenanceOptions = () => {
+    const isNotHeartBeating =
+      get(allHostModels, "[0].state", "") === "HEARTBEAT_LOST";
+    const hostName = get(allHostModels, "[0].hostName", "");
+    const isManualKerberos = false; // TODO: replace this hardcoded value with 
App.get('router.mainAdminKerberosController.isManualKerberos')
+    let result: any[] = [];
+
+    // SERVICE.START_STOP authorization check for component operations
+    if (canStartStopServices && !isUpgradeInProgress) {
+      result = result.concat([
+        {
+          label: translate("hosts.host.details.startAllComponents"),
+          isDisabled: isNotHeartBeating,
+          icon: "play",
+          iconClass: "text-success",
+          onClick: () => {
+            if (!isNotHeartBeating) {
+              doAction({
+                action: "startAllComponents",
+                hostName: hostName,
+                host: get(allHostModels, "[0]"),
+              });
+            }
+          },
+        },
+        {
+          label: translate("hosts.host.details.stopAllComponents"),
+          isDisabled: isNotHeartBeating,
+          icon: "stop",
+          iconClass: "text-danger",
+          onClick: () => {
+            if (!isNotHeartBeating) {
+              doAction({
+                action: "stopAllComponents",
+                hostName: hostName,
+                host: get(allHostModels, "[0]"),
+                clusterName: clusterName,
+              });
+            }
+          },
+        },
+        {
+          label: translate("hosts.host.details.restartAllComponents"),
+          isDisabled: isNotHeartBeating,
+          icon: "repeat",
+          onClick: () => {
+            if (!isNotHeartBeating) {
+              doAction({
+                action: "restartAllComponents",
+                hostName: hostName,
+                host: get(allHostModels, "[0]"),
+                clusterName: clusterName,
+              });
+            }
+          },
+        },
+      ]);
+    }
+
+    // HOST.TOGGLE_MAINTENANCE authorization check for maintenance operations
+    if (canToggleHostMaintenance && !isUpgradeInProgress) {
+      result = result.concat([
+        {
+          label: translate("hosts.host.details.setRackId"),
+          icon: "cog",
+          onClick: () => {
+            doAction({
+              action: "setRackId",
+              hostName: hostName,
+              clusterName: clusterName,
+              rack: get(allHostModels, "[0].rack", ""),
+              callback: setAllHostModels,
+            });
+          },
+        },
+        {
+          label: translate("passiveState.turn" + (isActive() ? "On" : "Off")),
+          icon: "medkit",
+          onClick: () => {
+            doAction({
+              action: "onOffPassiveModeForHost",
+              hostName: hostName,
+              host: get(allHostModels, "[0]"),
+              clusterStackVersion: stackVersionList,
+              clusterName: clusterName,
+              active: isActive(),
+              label: translate(
+                "passiveState.turn" + (isActive() ? "On" : "Off")
+              ),
+            });
+          },
+        },
+      ]);
+    }
+
+    if (
+      isKerberosEnabled &&
+      supports.regenerateKeytabsOnSingleHost &&
+      canToggleHostMaintenance &&
+      !isUpgradeInProgress
+    ) {
+      result.push({
+        label: translate("admin.kerberos.button.regenerateKeytabs"),
+        isVisible: isKerberosEnabled || isManualKerberos,
+        icon: "repeat",
+        onClick: () => {
+          doAction({
+            action: "regenerateKeytabFileOperations",
+            hostName: hostName,
+            clusterName: clusterName,
+          });
+        },
+      });
+    }
+
+    // HOST.ADD_DELETE_HOSTS authorization check for delete host operation
+    if (canAddDeleteHosts && !isUpgradeInProgress) {
+      result.push({
+        label: translate("hosts.host.details.deleteHost"),
+        icon: "remove",
+        iconClass: "text-danger",
+        onClick: () => {
+          // doAction({
+          //   action: "deleteHost",
+          //   hostName: hostName,
+          //   host: get(allHostModels, "[0]"),
+          //   clusterComponents: get(clusterComponents, "items", []),
+          //   serviceModels: serviceModels,
+          //   doDeleteHostComponent: _doDeleteHostComponent,
+          //   applyConfigsCustomization,
+          //   putConfigsToServer,
+          //   clearConfigsChanges,
+          //   loadComponentRelatedConfigs,
+          // });
+        },
+      });
+    }
+
+    // Admin/Operator authorization check for host check operation
+    if (canRunHostChecks) {
+      result.push({
+        label: translate("host.host.details.checkHost"),
+        icon: "check",
+        onClick: () => {
+          if (allHostModels.length) {
+            let modalProps = {
+              isOpen: true,
+              onClose: () => {},
+              modalTitle: translate("popup.confirmation.commonHeader"),
+              modalBody: translateWithVariables("hosts.checkHost.popup", {
+                "0": hostName,
+              }),
+              successCallback: () => {
+                setShowHostCheck(true);
+                modalManager.hide();
+              },
+              options: {
+                buttonSize: "sm" as "sm" | "lg" | undefined,
+                cancelableViaIcon: true,
+                cancelableViaBtn: true,
+                okButtonVariant: "primary",
+              },
+            };
+            modalManager.show(modalProps);
+          }
+        },
+      });
+    }
+
+    return result;
+  };
+
+  const getClients = () => {
+    // Only return client configs if user has permission to view configs
+    if (!canViewConfigs) {
+      return [];
+    }
+
+    let clients: any[] = [];
+    const hostName = get(allHostModels, "[0].hostName", "");
+    const hostComponents = get(allHostModels, "[0].hostComponents", []);
+    const clientComponents = hostComponents.filter((component: any) =>
+      get(component, "isClient", false)
+    );
+
+    forEach(clientComponents, (component: IHostComponent) => {
+      clients.push({
+        label: component.displayName,
+        onClick: () => {
+          doAction({
+            action: "downloadClientConfigs",
+            componentName: component.componentName,
+            hostName: hostName,
+            clusterName: clusterName,
+          });
+        },
+      });
+    });
+    if (clients.length > 1) {
+      clients.unshift({
+        label: translate("host.host.details.downloadAllClients"),
+        onClick: () => {
+          doAction({
+            action: "downloadAllClientConfigs",
+            hostName: hostName,
+            clusterName: clusterName,
+          });
+        },
+      });
+    }
+    return clients;
+  };
+
+  const buildHostActionsSubmenu = () => {
+    const submenu = [...getMaintenanceOptions()];
+
+    // Add client configs download if user has permission and there are clients
+    const clients = getClients();
+    if (canViewConfigs && clients.length > 0) {
+      submenu.push({
+        label: translate("services.service.actions.downloadClientConfigs"),
+        submenu: clients,
+        icon: "download",
+      });
+    }
+
+    // Add recover host - no authorization check needed (matches Ember.js 
pattern)
+    submenu.push({
+      action: "confirmRecoverHost",
+      label: translate("hosts.host.details.recoverHost"),
+      icon: "clockRotateLeft",
+      onClick: () => {
+        confirmRecoverHost({
+          host: get(allHostModels, "[0]"),
+          clusterName: clusterName,
+          isKerberosEnabled: isKerberosEnabled,
+          getKDCSessionState: getKDCSessionState,
+        });
+      },
+    });
+
+    return submenu;
+  };
+
+  const hostActionsMenu = {
+    label: String(translate("hosts.host.details.hostActions")).toUpperCase(),
+    submenu: buildHostActionsSubmenu(),
+  };
+
+  return (
+    <div className="p-4">
+      {showHostCheck ? (
+        <HostChecks
+          isOpen={showHostCheck}
+          onClose={() => {
+            setShowHostCheck(false);
+          }}
+          successCallback={() => {
+            startHostCheck(getBootHostsProp());
+          }}
+          loading={isHostCheckRunning}
+          hostCheckResult={hostCheckResult}
+        />
+      ) : null}
+      <Tab.Container activeKey={activeTab}>
+        <Row className="mx-5">
+          <Col>
+            <Nav
+              variant="underline"
+              onSelect={(selectedKey) => {
+                if (selectedKey && activeTab !== selectedKey) {
+                  setActiveTab(selectedKey);
+                  navigate(`/main/hosts/${params.hostname}/${selectedKey}`);
+                }
+              }}
+            >
+              {Object.entries(tabs).map(([key, tab]) => (
+                <Nav.Item>
+                  <Nav.Link eventKey={key} className="ambari-nav">
+                    {tab.title}
+                  </Nav.Link>
+                </Nav.Item>
+              ))}
+            </Nav>
+          </Col>
+          <Col className="d-flex justify-content-end">
+            {canShowHostActions && (
+              <NestedDropdown menu={hostActionsMenu} dropDirection="start" />
+            )}
+          </Col>
+        </Row>
+        <Row>
+          {loading ? (
+            <Spinner />
+          ) : (
+            <Col className="p-4">
+              {Object.entries(tabs).map(([key, tab]) => {
+                if (activeTab === key) {
+                  return tab.component;
+                }
+                return null;
+              })}
+            </Col>
+          )}
+        </Row>
+      </Tab.Container>
+    </div>
+  );
+}
diff --git 
a/ambari-web/latest/src/screens/Hosts/supportClientConfigsDownload.ts 
b/ambari-web/latest/src/screens/Hosts/supportClientConfigsDownload.ts
new file mode 100644
index 0000000000..26871b5d74
--- /dev/null
+++ b/ambari-web/latest/src/screens/Hosts/supportClientConfigsDownload.ts
@@ -0,0 +1,73 @@
+/**
+ * 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 { get } from "lodash";
+
+export enum ResourceTypeEnum {
+  CLUSTER = "ClusterResource",
+  HOST = "HostResource",
+  SERVICE = "ServiceResource",
+  SERVICE_COMPONENT = "ServiceComponentResource",
+  HOST_COMPONENT = "HostComponentResource",
+}
+
+export const downloadClientConfigsCall = (data: any) => {
+  const url = getUrl(
+    get(data, "clusterName"),
+    get(data, "hostName", ""),
+    get(data, "serviceName", ""),
+    get(data, "componentName", ""),
+    get(data, "resourceType")
+  );
+  const newWindow = window.open(url);
+  if (newWindow) {
+    newWindow.focus();
+  }
+};
+
+const getUrl = (
+  clusterName: string,
+  hostName: string,
+  serviceName: string,
+  componentName: string,
+  resourceType: ResourceTypeEnum
+) => {
+  const apiPrefix = "/api/v1";
+  let result = `${apiPrefix}/clusters/${clusterName}/`;
+
+  switch (resourceType) {
+    case ResourceTypeEnum.SERVICE_COMPONENT:
+      result += `services/${serviceName}/components/${componentName}`;
+      break;
+    case ResourceTypeEnum.HOST_COMPONENT:
+      result += `hosts/${hostName}/host_components/${componentName}`;
+      break;
+    case ResourceTypeEnum.HOST:
+      result += `hosts/${hostName}/host_components`;
+      break;
+    case ResourceTypeEnum.SERVICE:
+      result += `services/${serviceName}/components`;
+      break;
+    case ResourceTypeEnum.CLUSTER:
+    default:
+      result += "components";
+  }
+
+  result += "?format=client_config_tar";
+  return result;
+};


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

Reply via email to