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 4544eb6342 AMBARI:-26582: Ambari Web React: Add recommendation modal 
for add/delete components on hosts (#4107)
4544eb6342 is described below

commit 4544eb63420f47dcd7da18807a1c5654123a8c09
Author: Sandeep  Kumar <[email protected]>
AuthorDate: Tue Jan 27 13:55:10 2026 +0530

    AMBARI:-26582: Ambari Web React: Add recommendation modal for add/delete 
components on hosts (#4107)
---
 ambari-web/latest/src/components/Modal.tsx         |   2 +-
 .../latest/src/components/RecommendationModal.tsx  | 282 +++++++++++++++++++++
 2 files changed, 283 insertions(+), 1 deletion(-)

diff --git a/ambari-web/latest/src/components/Modal.tsx 
b/ambari-web/latest/src/components/Modal.tsx
index 2c14e5114e..de107dba60 100644
--- a/ambari-web/latest/src/components/Modal.tsx
+++ b/ambari-web/latest/src/components/Modal.tsx
@@ -32,7 +32,7 @@ export type ModalProps = {
     shouldShowFooter?: boolean;
     buttonSize?: "sm" | "lg" | undefined;
     okButtonText?: any;
-    cancelButtonText?: string;
+    cancelButtonText?: any;
     cancelableViaIcon?: boolean;
     cancelableViaBtn?: boolean;
     cancelableViaSuccessBtn?: boolean;
diff --git a/ambari-web/latest/src/components/RecommendationModal.tsx 
b/ambari-web/latest/src/components/RecommendationModal.tsx
new file mode 100644
index 0000000000..80610dad97
--- /dev/null
+++ b/ambari-web/latest/src/components/RecommendationModal.tsx
@@ -0,0 +1,282 @@
+/**
+ * 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 { cloneDeep } from "lodash";
+import { useEffect, useState } from "react";
+import { DropdownButton, DropdownItem, Form } from "react-bootstrap";
+import Table from "./Table";
+import Modal from "./Modal";
+import { translate, translateWithVariables } from "../Utils/Utility";
+
+type RecommendationModalProps = {
+  isOpen: boolean;
+  onClose: () => void;
+  componentDisplayName?: string;
+  add: boolean;
+  recommendedPropertiesToChange?: any;
+  callback: (properties?: any) => void;
+  fromService?: boolean;
+  serviceName?: string;
+  componentName?: string;
+  validDropDownHosts?: string[];
+  handleHostChange?: (host: string) => void;
+  commonMessage?: string;
+  selectedHostForDropDown?: string | null;
+   selectRecommendedProperties?: (newProperties:any)=>void
+};
+export default function RecommendationModal({
+  isOpen,
+  onClose,
+  componentDisplayName,
+  add,
+  recommendedPropertiesToChange,
+  //@ts-ignore
+  callback = (properties?: any) => {},
+  fromService,
+  serviceName,
+  componentName,
+  validDropDownHosts,
+  handleHostChange,
+  commonMessage = "",
+  selectedHostForDropDown,
+   selectRecommendedProperties
+}: RecommendationModalProps) {
+  const [isAllChecked, setIsAllChecked] = useState(false);
+  const [propertiesToChange, setPropertiesToChange] = useState<any[]>(
+    recommendedPropertiesToChange || []
+  );
+  const [selectedHost, setSelectedHost] = useState<string | 
null>(selectedHostForDropDown || null);
+  useEffect(() => {
+    setIsAllChecked(
+      propertiesToChange.every((property: any) => property.saveRecommended)
+    );
+  }, [propertiesToChange]);
+
+  const handleHostSelection = (host: any) => {
+    setSelectedHost(host); // Update internal state immediately to show 
selection in UI
+    if (handleHostChange) {
+      handleHostChange(host); // Trigger callback directly
+    }
+  };
+
+  const handleHeaderCheckboxChange = (
+    event: React.ChangeEvent<HTMLInputElement>
+  ) => {
+    const isChecked = event.target.checked;
+
+    const updatedProperties = propertiesToChange.map((property: any) => ({
+      ...property,
+      saveRecommended: isChecked,
+    }));
+    
+    setPropertiesToChange(updatedProperties);
+    if(selectRecommendedProperties){
+      selectRecommendedProperties(updatedProperties);
+    }
+  };
+
+  const handleRowCheckboxChange = (index: number, event: any): any => {
+    const isChecked = event.target.checked;
+
+    const newProperties = cloneDeep(propertiesToChange);
+    newProperties[index].saveRecommended = isChecked;
+    setPropertiesToChange(newProperties);
+    if(selectRecommendedProperties){
+      selectRecommendedProperties(newProperties);
+    }
+  };
+
+  function getModalBody() {
+    // Use serviceName if available, otherwise use componentName
+    const displayComponentName = serviceName || componentName || 
componentDisplayName;
+    const addHostFromServiceMessage = `Select the host to add 
${displayComponentName} component: `;
+
+    const dropDownButtonToAddValidHosts = (
+      <DropdownButton
+        title={
+          selectedHost
+            ? selectedHost
+            : validDropDownHosts && validDropDownHosts.length > 0
+            ? validDropDownHosts[0]
+            : ""
+        }
+      >
+        {validDropDownHosts?.map((host) => {
+          return (
+            <DropdownItem
+              key={host}
+              onClick={() => {
+                handleHostSelection(host);
+              }}
+            >
+              {host}
+            </DropdownItem>
+          );
+        })}
+      </DropdownButton>
+    );
+
+    const message = !add
+      ? commonMessage
+        ? commonMessage
+        : translateWithVariables("hosts.host.deleteComponent.popup.msg1", {
+            "0": componentDisplayName || "",
+          })
+      : translateWithVariables("hosts.host.addComponent.msg", {
+          "0": componentDisplayName || "",
+        });
+
+    if (propertiesToChange && propertiesToChange.length > 0) {
+      const columns = [
+        {
+          accessorKey: "saveRecommended",
+          header: () => (
+            <Form.Check
+              id="select-all-checkbox"
+              type="checkbox"
+              checked={isAllChecked}
+              onChange={handleHeaderCheckboxChange}
+            />
+          ),
+          cell: ({ row }: { row: any }) => (
+            <Form.Check
+              type="checkbox"
+              checked={row.original.saveRecommended}
+              onChange={(e) => {
+                handleRowCheckboxChange(row.index, e);
+              }}
+            />
+          ),
+          width: "5%",
+        },
+        {
+          accessorKey: "propertyName",
+          header: "Property Name",
+          width: "15%",
+        },
+        {
+          accessorKey: "serviceDisplayName",
+          header: "Service",
+          width: "5%",
+        },
+        {
+          accessorKey: "configGroup",
+          header: "Config Group",
+          cell: ({ getValue }: { getValue: () => any }) => getValue() || "",
+          width: "10%",
+        },
+        {
+          accessorKey: "propertyFileName",
+          header: "File Name",
+          width: "10%",
+        },
+        {
+          accessorKey: "initialValue",
+          header: "Original Value",
+          width: "30%",
+        },
+        {
+          accessorKey: "recommendedValue",
+          header: "Recommended Value",
+          width: "30%",
+        },
+      ];
+      return (
+        <div>
+          {fromService ? (
+            <>
+              <div className="d-flex justfiy-content-between">
+                <div className="fs-6">{addHostFromServiceMessage}</div>
+                <div className="mx-2">{dropDownButtonToAddValidHosts}</div>
+                <div></div>
+              </div>
+              <div className="m-2 fs-12">{message}</div>
+            </>
+          ) : (
+            <>
+              <div className="fs-12">{message}</div>
+            </>
+          )}
+
+          <h3 className="mt-2">
+            {translate("popup.dependent.configs.table.recommended")}
+          </h3>
+          <div className="mb-3 alert alert-warning fs-12">
+            {translate("popup.dependent.configs.title.values")}
+          </div>
+
+          <Table
+            columns={columns}
+            data={propertiesToChange}
+            entityName="property"
+            hover
+          />
+        </div>
+      );
+    }
+    return (
+      <div>
+        {fromService ? (
+          <>
+            <div className="d-flex justfiy-content-between">
+              <div className="fs-6">{addHostFromServiceMessage}</div>
+              <div className="mx-2">{dropDownButtonToAddValidHosts}</div>
+              <div></div>
+            </div>
+            <div className="m-2">{message}</div>
+          </>
+        ) : (
+          <div>{message}</div>
+        )}
+      </div>
+    );
+  }
+
+  function getOkButtonText() {
+    return add
+      ? translate("hosts.host.addComponent.popup.confirm")
+      : translate("hosts.host.deleteComponent.popup.confirm");
+  }
+
+  return (
+    <Modal
+      isOpen={isOpen}
+      className={propertiesToChange.length > 0?"bg-operations-modal":""}
+      onClose={onClose}
+      modalTitle={translate("popup.confirmation.commonHeader")}
+      modalBody={getModalBody()}
+      successCallback={() => {
+        onClose();
+        if (recommendedPropertiesToChange) {
+          callback(propertiesToChange);
+        } else {
+          callback();
+        }
+      }}
+      options={{
+        modalSize: "modal-lg",
+        buttonSize: "sm",
+        okButtonText: getOkButtonText(),
+        cancelButtonText: translate("popup.confirmation.cancel"),
+        cancelableViaIcon: true,
+        cancelableViaBtn: true,
+        okButtonVariant: "primary",
+      }}
+    />
+  );
+}


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

Reply via email to