Repository: red-hat-storage/odf-console
Pull request #1856: RHSTOR-6968: Disaster recovery UI for Kubevirt VM - managed application
Author: login: GowthamShanmugam
Labels: name: approved
name: lgtm
name: jira/valid-reference
description: Indicates that this PR references a valid jira ticket of any type
Created: 2025-02-12T12:59:35Z | Merged: 2025-03-13T08:48:51Z

ACM custom extension: https://github.com/stolostron/console/pull/4252

**Existing Flow:**
The parser always triggers the modal, even while the watcher is fetching data, displaying it with a loading: false argument.

<img width="934" alt="Screenshot 2025-02-17 at 6 10 29 PM" src="https://github.com/user-attachments/assets/df0b0add-a1a4-4219-8198-7dd428116176" />


**Problem in existing flow for VM:** (Delay in modal loading)
The application parser is determined only after the VM parser identifies the appropriate parser to call. This causes a delay in modal loading, making the modal appear a few seconds after the user clicks the button.

<img width="947" alt="Screenshot 2025-02-17 at 6 10 43 PM" src="https://github.com/user-attachments/assets/a215c921-44e8-461a-9351-00b779a78f3b" />


**New Flow:**
The modal will always load, while the modal body will be rendered based on the parsers.

<img width="976" alt="Screenshot 2025-02-17 at 6 24 27 PM" src="https://github.com/user-attachments/assets/621916db-016d-46dd-921e-38350bc55aaa" />









Diff:
diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json
index fb97c911c..d6a2aacfb 100644
--- a/locales/en/plugin__odf-console.json
+++ b/locales/en/plugin__odf-console.json
@@ -342,7 +342,7 @@
   "Select the subscriptions groups you wish to replicate via": "Select the subscriptions groups you wish to replicate via",
   "Enroll managed application": "Enroll managed application",
   "Manage disaster recovery": "Manage disaster recovery",
-  "<0>Application:</0> {getName(applicationInfo)} (Namespace: {getNamespace(applicationInfo)})": "<0>Application:</0> {getName(applicationInfo)} (Namespace: {getNamespace(applicationInfo)})",
+  "<0>Application:</0> {applicationName} (Namespace: {applicationNamespace})": "<0>Application:</0> {applicationName} (Namespace: {applicationNamespace})",
   "Assign policy nav": "Assign policy nav",
   "Assign policy content": "Assign policy content",
   "Labels must start and end with an alphanumeric character, can consist of lower-case letters, numbers, dots (.), hyphens (-), forward slash (/), underscore(_) and equal to (=)": "Labels must start and end with an alphanumeric character, can consist of lower-case letters, numbers, dots (.), hyphens (-), forward slash (/), underscore(_) and equal to (=)",
diff --git a/packages/mco/components/modals/app-manage-policies/acm-action-callback.tsx b/packages/mco/components/modals/app-manage-policies/acm-action-callback.tsx
deleted file mode 100644
index d0507afd9..000000000
--- a/packages/mco/components/modals/app-manage-policies/acm-action-callback.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import * as React from 'react';
-import { ArgoApplicationSetModel } from '@odf/mco/models';
-import { ACMApplicationKind, ArgoApplicationSetKind } from '@odf/mco/types';
-import { ApplicationModel } from '@odf/shared/models';
-import { ApplicationKind } from '@odf/shared/types';
-import { getGVKofResource, referenceForModel } from '@odf/shared/utils';
-import { ApplicationSetParser } from './parsers/application-set-parser';
-import { SubscriptionParser } from './parsers/subscription-parser';
-
-const AppManageDataPolicy: React.FC<ACMActionCallbackProps> = ({
-  resource,
-  close,
-  isOpen,
-}) => {
-  const gvk = getGVKofResource(resource);
-  return (
-    <>
-      {gvk === referenceForModel(ArgoApplicationSetModel) && (
-        <ApplicationSetParser
-          application={resource as ArgoApplicationSetKind}
-          isOpen={isOpen}
-          close={close}
-        />
-      )}
-      {gvk === referenceForModel(ApplicationModel) && (
-        <SubscriptionParser
-          application={resource as ApplicationKind}
-          isOpen={isOpen}
-          close={close}
-        />
-      )}
-    </>
-  );
-};
-
-export type ACMActionCallbackProps = {
-  isOpen: boolean;
-  resource: ACMApplicationKind;
-  close: () => void;
-};
-
-export default AppManageDataPolicy;
diff --git a/packages/mco/components/modals/app-manage-policies/app-manage-policies-modal-body.tsx b/packages/mco/components/modals/app-manage-policies/app-manage-policies-modal-body.tsx
new file mode 100644
index 000000000..dfeec2b23
--- /dev/null
+++ b/packages/mco/components/modals/app-manage-policies/app-manage-policies-modal-body.tsx
@@ -0,0 +1,40 @@
+import * as React from 'react';
+import { ArgoApplicationSetModel, VirtualMachineModel } from '@odf/mco/models';
+import { ApplicationModel } from '@odf/shared/models';
+import { getGVKofResource, referenceForModel } from '@odf/shared/utils';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  VirtualMachineParser,
+} from './parsers';
+import { ModalViewContext } from './utils/reducer';
+
+const ComponentMap = {
+  [referenceForModel(ArgoApplicationSetModel)]: ApplicationSetParser,
+  [referenceForModel(ApplicationModel)]: SubscriptionParser,
+  [referenceForModel(VirtualMachineModel)]: VirtualMachineParser,
+};
+
+export const AppManagePoliciesModalBody: React.FC<
+  AppManagePoliciesModalBodyProps
+> = ({ application, cluster, setCurrentModalContext }) => {
+  const gvk = getGVKofResource(application);
+  const SelectedComponent = ComponentMap[gvk];
+
+  return SelectedComponent ? (
+    <SelectedComponent
+      application={application as any}
+      cluster={cluster}
+      setCurrentModalContext={setCurrentModalContext}
+    />
+  ) : null;
+};
+
+type AppManagePoliciesModalBodyProps = {
+  application: K8sResourceCommon;
+  cluster?: string;
+  setCurrentModalContext: React.Dispatch<
+    React.SetStateAction<ModalViewContext>
+  >;
+};
diff --git a/packages/mco/components/modals/app-manage-policies/app-manage-policies-modal.tsx b/packages/mco/components/modals/app-manage-policies/app-manage-policies-modal.tsx
index d45fb7f6f..21ae3119b 100644
--- a/packages/mco/components/modals/app-manage-policies/app-manage-policies-modal.tsx
+++ b/packages/mco/components/modals/app-manage-policies/app-manage-policies-modal.tsx
@@ -1,144 +1,70 @@
 import * as React from 'react';
-import { DRApplication } from '@odf/mco/constants';
+import { SearchResultItemType } from '@odf/mco/types';
+import { convertSearchResult } from '@odf/mco/utils';
 import { getName, getNamespace } from '@odf/shared/selectors';
 import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
 import { Trans } from 'react-i18next';
 import { Modal, ModalVariant } from '@patternfly/react-core';
-import { AssignPolicyView } from './assign-policy-view';
-import { ManagePolicyView } from './manage-policy-view';
-import {
-  ManagePolicyStateAction,
-  ManagePolicyStateType,
-  ModalActionContext,
-  ModalViewContext,
-  initialPolicyState,
-  managePolicyStateReducer,
-  ManagePolicyState,
-} from './utils/reducer';
-import { ApplicationType, DRInfoType, DRPolicyType } from './utils/types';
+import { AppManagePoliciesModalBody } from './app-manage-policies-modal-body';
+import { ModalViewContext } from './utils/reducer';
 
-export const ModalContextViewer: React.FC<ModalContextViewerProps> = ({
-  applicationInfo,
-  state,
-  matchingPolicies,
-  dispatch,
-  loaded,
-  loadError,
+export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
+  resource,
+  cluster,
+  isOpen,
+  close,
 }) => {
-  const setModalContext = React.useCallback(
-    (modalViewContext: ModalViewContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
-        payload: modalViewContext,
-      }),
-    [dispatch]
+  const { t } = useCustomTranslation();
+  const [currentModalContext, setCurrentModalContext] = React.useState(
+    ModalViewContext.MANAGE_POLICY_VIEW
   );
 
-  const setModalActionContext = React.useCallback(
-    (modalActionContext: ModalActionContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
-        payload: modalActionContext,
-      }),
-    [dispatch]
-  );
+  const application =
+    'apigroup' in resource ? convertSearchResult(resource) : resource;
+  const applicationName = getName(application) ?? application?.['name'];
+  const applicationNamespace =
+    getNamespace(application) ?? application?.['namespace'];
 
-  return (
-    <>
-      {state.modalViewContext === ModalViewContext.MANAGE_POLICY_VIEW && (
-        <ManagePolicyView
-          drInfo={applicationInfo?.drInfo as DRInfoType}
-          workloadNamespace={applicationInfo?.workloadNamespace}
-          eligiblePolicies={matchingPolicies}
-          isSubscriptionAppType={
-            applicationInfo?.type === DRApplication.SUBSCRIPTION
-          }
-          unProtectedPlacementCount={applicationInfo?.placements?.length}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-          loaded={loaded}
-          loadError={loadError}
-          modalActionContext={state.modalActionContext}
-        />
-      )}
-      {state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW && (
-        <AssignPolicyView
-          applicationInfo={applicationInfo}
-          matchingPolicies={matchingPolicies}
-          state={state.assignPolicyView}
-          modalActionContext={state.modalActionContext}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-        />
-      )}
-    </>
-  );
-};
+  const title =
+    currentModalContext === ModalViewContext.ASSIGN_POLICY_VIEW
+      ? t('Enroll managed application')
+      : t('Manage disaster recovery');
 
-export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
-  applicationInfo,
-  matchingPolicies,
-  loaded,
-  loadError,
-  isOpen,
-  close,
-}) => {
-  const [state, dispatch] = React.useReducer(
-    managePolicyStateReducer,
-    initialPolicyState
+  const description = (
+    <Trans t={t}>
+      <strong>Application:</strong> {applicationName} (Namespace:{' '}
+      {applicationNamespace})
+    </Trans>
   );
-  const { t } = useCustomTranslation();
 
   return (
     <Modal
-      title={
-        state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW
-          ? t('Enroll managed application')
-          : t('Manage disaster recovery')
-      }
-      description={
-        loaded &&
-        !loadError && (
-          <Trans t={t}>
-            <strong>Application:</strong> {getName(applicationInfo)} (Namespace:{' '}
-            {getNamespace(applicationInfo)})
-          </Trans>
-        )
-      }
+      title={title}
+      description={description}
       variant={ModalVariant.large}
       isOpen={isOpen}
       aria-label="Manage policy modal"
       aria-describedby="manage-policy-modal"
       onClose={close}
     >
-      <ModalContextViewer
-        applicationInfo={applicationInfo}
-        matchingPolicies={matchingPolicies}
-        state={state}
-        dispatch={dispatch}
-        loaded={loaded}
-        loadError={loadError}
+      <AppManagePoliciesModalBody
+        application={application}
+        cluster={cluster}
+        setCurrentModalContext={setCurrentModalContext}
       />
     </Modal>
   );
 };
 
+// ACM action custom plugin callback,
+// For more: https://github.com/stolostron/console/blob/main/frontend/src/plugin-extensions/properties/actionExtensionProps.ts#L27
 type AppManagePoliciesModalProps = {
-  applicationInfo: ApplicationType;
-  matchingPolicies: DRPolicyType[];
-  loaded: boolean;
-  loadError: any;
+  resource: K8sResourceCommon | SearchResultItemType;
   isOpen: boolean;
-  close?: () => void;
+  close: () => void;
+  // Specific field for the ACM VM list page
+  cluster?: string;
 };
 
-type ModalContextViewerProps = {
-  state: ManagePolicyState;
-  applicationInfo: ApplicationType;
-  matchingPolicies: DRPolicyType[];
-  dispatch: React.Dispatch<ManagePolicyStateAction>;
-  loaded: boolean;
-  loadError: any;
-};
+export default AppManagePoliciesModal;
diff --git a/packages/mco/components/modals/app-manage-policies/assign-policy-view.tsx b/packages/mco/components/modals/app-manage-policies/assign-policy-view.tsx
index e728d7596..0f5d1df42 100644
--- a/packages/mco/components/modals/app-manage-policies/assign-policy-view.tsx
+++ b/packages/mco/components/modals/app-manage-policies/assign-policy-view.tsx
@@ -28,11 +28,11 @@ import {
   DRInfoType,
   DRPolicyType,
   PlacementType,
+  PVCQueryFilter,
 } from './utils/types';
 
 export const createSteps = (
   appType: DRApplication,
-  workloadNamespace: string,
   unProtectedPlacements: PlacementType[],
   matchingPolicies: DRPolicyType[],
   state: AssignPolicyViewState,
@@ -41,6 +41,7 @@ export const createSteps = (
   t: TFunction,
   dispatch: React.Dispatch<ManagePolicyStateAction>,
   protectedPVCSelectors: PVCSelectorType[],
+  pvcQueryFilter: PVCQueryFilter,
   isEditMode?: boolean
 ): WizardStep[] => {
   const commonSteps = {
@@ -61,10 +62,10 @@ export const createSteps = (
         <PVCDetailsWizardContent
           pvcSelectors={state.persistentVolumeClaim.pvcSelectors}
           unProtectedPlacements={unProtectedPlacements}
-          workloadNamespace={workloadNamespace}
           isValidationEnabled={isValidationEnabled}
           dispatch={dispatch}
           protectedPVCSelectors={protectedPVCSelectors}
+          pvcQueryFilter={pvcQueryFilter}
         />
       ),
     },
@@ -130,9 +131,9 @@ export const AssignPolicyView: React.FC<AssignPolicyViewProps> = ({
 
   const {
     type: appType,
-    workloadNamespace,
     placements: unProtectedPlacements,
     drInfo,
+    pvcQueryFilter,
   } = applicationInfo;
 
   const protectedPVCSelectors: PVCSelectorType[] = isEditMode
@@ -179,7 +180,6 @@ export const AssignPolicyView: React.FC<AssignPolicyViewProps> = ({
         mainAriaLabel={t('Assign policy content')}
         steps={createSteps(
           appType,
-          workloadNamespace,
           unProtectedPlacements,
           matchingPolicies,
           state,
@@ -188,6 +188,7 @@ export const AssignPolicyView: React.FC<AssignPolicyViewProps> = ({
           t,
           dispatch,
           protectedPVCSelectors,
+          pvcQueryFilter,
           isEditMode
         )}
         footer={
diff --git a/packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx b/packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx
index 5cf682d12..246ce0c10 100644
--- a/packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx
+++ b/packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx
@@ -35,14 +35,13 @@ import {
 } from '@patternfly/react-core';
 import { MinusCircleIcon } from '@patternfly/react-icons';
 import { queryAppWorkloadPVCs } from '../../../../utils/acm-search-queries';
-import { getClusterNamesFromPlacements } from '../utils/parser-utils';
 import {
   ManagePolicyStateAction,
   ManagePolicyStateType,
   ModalViewContext,
   PVCSelectorType,
 } from '../utils/reducer';
-import { PlacementType } from '../utils/types';
+import { PlacementType, PVCQueryFilter } from '../utils/types';
 import {
   getLabelValidationMessage,
   isValidLabelInput,
@@ -61,14 +60,17 @@ const getPlacementTags = (pvcSelectors: PVCSelectorType[]): TagsType =>
 
 const getLabelsFromSearchResult = (searchResult: SearchResult): string[] => {
   const pvcLabels =
-    searchResult?.data.searchResult?.[0]?.items.reduce((acc, item) => {
-      const labels: string[] = item[LABEL]?.split(LABELS_SPLIT_CHAR) || [];
-      const filteredLabels = labels?.filter((label) => {
-        const [key] = label.split(LABEL_SPLIT_CHAR);
-        return !DR_BLOCK_LISTED_LABELS.includes(key);
-      });
-      return [...acc, ...filteredLabels];
-    }, []) || [];
+    searchResult?.data.searchResult?.[0]?.related?.[0]?.items?.reduce(
+      (acc, item) => {
+        const labels: string[] = item[LABEL]?.split(LABELS_SPLIT_CHAR) || [];
+        const filteredLabels = labels?.filter((label) => {
+          const [key] = label.split(LABEL_SPLIT_CHAR);
+          return !DR_BLOCK_LISTED_LABELS.includes(key);
+        });
+        return [...acc, ...filteredLabels];
+      },
+      []
+    ) || [];
   return Array.from(new Set(pvcLabels));
 };
 
@@ -250,10 +252,10 @@ export const PVCDetailsWizardContent: React.FC<
 > = ({
   pvcSelectors,
   unProtectedPlacements,
-  workloadNamespace,
   isValidationEnabled,
   protectedPVCSelectors,
   dispatch,
+  pvcQueryFilter,
 }) => {
   const { t } = useCustomTranslation();
 
@@ -270,12 +272,8 @@ export const PVCDetailsWizardContent: React.FC<
 
   // ACM search proxy api call
   const searchQuery = React.useMemo(
-    () =>
-      queryAppWorkloadPVCs(
-        workloadNamespace,
-        getClusterNamesFromPlacements(unProtectedPlacements)
-      ),
-    [unProtectedPlacements, workloadNamespace]
+    () => queryAppWorkloadPVCs(pvcQueryFilter),
+    [pvcQueryFilter]
   );
   const [searchResult, error, loaded] = useACMSafeFetch(searchQuery);
 
@@ -355,8 +353,8 @@ type ExtraProps = {
 type PVCDetailsWizardContentProps = {
   pvcSelectors: PVCSelectorType[];
   unProtectedPlacements: PlacementType[];
-  workloadNamespace: string;
   isValidationEnabled: boolean;
   dispatch: React.Dispatch<ManagePolicyStateAction>;
   protectedPVCSelectors: PVCSelectorType[];
+  pvcQueryFilter: PVCQueryFilter;
 };
diff --git a/packages/mco/components/modals/app-manage-policies/manage-policy-view.tsx b/packages/mco/components/modals/app-manage-policies/manage-policy-view.tsx
index 47faa79e1..3b43ac705 100644
--- a/packages/mco/components/modals/app-manage-policies/manage-policy-view.tsx
+++ b/packages/mco/components/modals/app-manage-policies/manage-policy-view.tsx
@@ -294,7 +294,7 @@ export const ManagePolicyView: React.FC<ManagePolicyViewProps> = ({
   workloadNamespace,
   eligiblePolicies,
   isSubscriptionAppType,
-  unProtectedPlacementCount,
+  unprotectedPlacementCount,
   modalActionContext,
   loaded,
   loadError,
@@ -373,7 +373,7 @@ export const ManagePolicyView: React.FC<ManagePolicyViewProps> = ({
       <ModalBody>
         <DRInformation
           dataPolicyInfo={drInfo}
-          hideEditAction={!isSubscriptionAppType || !unProtectedPlacementCount}
+          hideEditAction={!isSubscriptionAppType || !unprotectedPlacementCount}
           onEdit={onEdit}
         />
         {!!alertProps && (
@@ -427,7 +427,7 @@ type ManagePolicyViewProps = {
   workloadNamespace: string;
   eligiblePolicies: DRPolicyType[];
   isSubscriptionAppType: boolean;
-  unProtectedPlacementCount: number;
+  unprotectedPlacementCount: number;
   modalActionContext: ModalActionContext;
   loaded: boolean;
   loadError: any;
diff --git a/packages/mco/components/modals/app-manage-policies/modal-context-viewer.tsx b/packages/mco/components/modals/app-manage-policies/modal-context-viewer.tsx
new file mode 100644
index 000000000..600741f58
--- /dev/null
+++ b/packages/mco/components/modals/app-manage-policies/modal-context-viewer.tsx
@@ -0,0 +1,100 @@
+import * as React from 'react';
+import { DRApplication } from '@odf/mco/constants';
+import { AssignPolicyView } from './assign-policy-view';
+import { ManagePolicyView } from './manage-policy-view';
+import {
+  initialPolicyState,
+  managePolicyStateReducer,
+  ManagePolicyStateType,
+  ModalActionContext,
+  ModalViewContext,
+} from './utils/reducer';
+import {
+  ApplicationInfoType,
+  ApplicationType,
+  DRInfoType,
+  DRPolicyType,
+} from './utils/types';
+
+export const ModalContextViewer: React.FC<ModalContextViewerProps> = ({
+  applicationInfo,
+  matchingPolicies,
+  loaded,
+  loadError,
+  setCurrentModalContext,
+}) => {
+  const [state, dispatch] = React.useReducer(
+    managePolicyStateReducer,
+    initialPolicyState
+  );
+
+  const setModalContext = React.useCallback(
+    (modalViewContext: ModalViewContext) => {
+      dispatch({
+        type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
+        payload: modalViewContext,
+      });
+      setCurrentModalContext(modalViewContext);
+    },
+    [setCurrentModalContext]
+  );
+
+  const setModalActionContext = React.useCallback(
+    (modalActionContext: ModalActionContext) =>
+      dispatch({
+        type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
+        payload: modalActionContext,
+      }),
+    []
+  );
+
+  const application = applicationInfo as ApplicationType;
+
+  const renderView = () => {
+    if (state.modalViewContext === ModalViewContext.MANAGE_POLICY_VIEW) {
+      return (
+        <ManagePolicyView
+          drInfo={application?.drInfo as DRInfoType}
+          workloadNamespace={application?.workloadNamespace}
+          eligiblePolicies={matchingPolicies}
+          isSubscriptionAppType={
+            application?.type === DRApplication.SUBSCRIPTION
+          }
+          unprotectedPlacementCount={application?.placements?.length}
+          dispatch={dispatch}
+          setModalContext={setModalContext}
+          setModalActionContext={setModalActionContext}
+          loaded={loaded}
+          loadError={loadError}
+          modalActionContext={state.modalActionContext}
+        />
+      );
+    }
+    if (state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW) {
+      return (
+        <AssignPolicyView
+          applicationInfo={application}
+          matchingPolicies={matchingPolicies}
+          state={state.assignPolicyView}
+          modalActionContext={state.modalActionContext}
+          dispatch={dispatch}
+          setModalContext={setModalContext}
+          setModalActionContext={setModalActionContext}
+        />
+      );
+    }
+    return null;
+  };
+
+  return <>{renderView()}</>;
+};
+
+type ModalContextViewerProps = {
+  applicationInfo: ApplicationInfoType;
+  matchingPolicies: DRPolicyType[];
+  loaded: boolean;
+  loadError: any;
+  setCurrentModalContext: React.Dispatch<
+    React.SetStateAction<ModalViewContext>
+  >;
+};
diff --git a/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.spec.tsx b/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.spec.tsx
index 2e2ca4767..a7733585c 100644
--- a/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.spec.tsx
+++ b/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.spec.tsx
@@ -28,7 +28,7 @@ import {
 import { ArgoApplicationSetResourceKind } from '../../../../hooks';
 import { DisasterRecoveryResourceKind } from '../../../../hooks/disaster-recovery';
 import { SearchResult } from '../../../../types';
-import { ApplicationSetParser } from './application-set-parser';
+import AppManagePoliciesModal from '../app-manage-policies-modal';
 
 let testCase = 1;
 
@@ -36,16 +36,20 @@ const searchResult: SearchResult = {
   data: {
     searchResult: [
       {
-        items: [
+        related: [
           {
-            apiversion: 'v1',
-            cluster: 'local-cluster',
-            created: '2023-07-04T17:14:10Z',
-            kind: 'PersistentVolumeClaim',
-            label: 'app=mock-appset-2',
-            name: 'busybox-pvc',
-            namespace: 'test-ns',
-            _uid: 'local-cluster/683b0a87-85bf-4743-96d2-565863752e53',
+            items: [
+              {
+                apiversion: 'v1',
+                cluster: 'local-cluster',
+                created: '2023-07-04T17:14:10Z',
+                kind: 'PersistentVolumeClaim',
+                label: 'app=mock-appset-2',
+                name: 'busybox-pvc',
+                namespace: 'test-ns',
+                _uid: 'local-cluster/683b0a87-85bf-4743-96d2-565863752e53',
+              },
+            ],
           },
         ],
       },
@@ -186,8 +190,8 @@ describe('ApplicationSet manage disaster recovery modal', () => {
   test('Empty manage policy page test', async () => {
     testCase = 1;
     render(
-      <ApplicationSetParser
-        application={mockApplicationSet2}
+      <AppManagePoliciesModal
+        resource={mockApplicationSet2}
         close={onClose}
         isOpen={true}
       />
@@ -208,8 +212,8 @@ describe('ApplicationSet manage disaster recovery modal', () => {
   test('manage policy view test', async () => {
     testCase = 2;
     render(
-      <ApplicationSetParser
-        application={mockApplicationSet1}
+      <AppManagePoliciesModal
+        resource={mockApplicationSet1}
         close={onClose}
         isOpen={true}
       />
@@ -239,8 +243,8 @@ describe('ApplicationSet manage disaster recovery modal', () => {
     const user = userEvent.setup();
     testCase = 3;
     render(
-      <ApplicationSetParser
-        application={mockApplicationSet2}
+      <AppManagePoliciesModal
+        resource={mockApplicationSet2}
         close={onClose}
         isOpen={true}
       />
diff --git a/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.tsx b/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.tsx
index 701dbb9d1..7e750129d 100644
--- a/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.tsx
+++ b/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.tsx
@@ -2,13 +2,11 @@ import * as React from 'react';
 import { DRApplication, PLACEMENT_REF_LABEL } from '@odf/mco/constants';
 import {
   DisasterRecoveryResourceKind,
-  getDRClusterResourceObj,
-  getDRPlacementControlResourceObj,
-  getDRPolicyResourceObj,
   getPlacementDecisionsResourceObj,
   getPlacementResourceObj,
   useArgoApplicationSetResourceWatch,
   useDisasterRecoveryResourceWatch,
+  getApplicationSetResourceObj,
 } from '@odf/mco/hooks';
 import { ArgoApplicationSetKind } from '@odf/mco/types';
 import {
@@ -17,50 +15,59 @@ import {
   findDeploymentClusters,
 } from '@odf/mco/utils';
 import { useDeepCompareMemoize } from '@odf/shared';
-import { getNamespace } from '@odf/shared/selectors';
+import { getName, getNamespace } from '@odf/shared/selectors';
 import * as _ from 'lodash-es';
-import { AppManagePoliciesModal } from '../app-manage-policies-modal';
+import { ModalContextViewer } from '../modal-context-viewer';
 import {
   generateApplicationInfo,
   generateDRPlacementControlInfo,
   generateDRInfo,
   generatePlacementInfo,
   getMatchingDRPolicies,
+  getDRResources,
 } from '../utils/parser-utils';
+import { ModalViewContext } from '../utils/reducer';
 import {
   ApplicationInfoType,
   ApplicationType,
   DRPlacementControlType,
   DRPolicyType,
+  PVCQueryFilter,
 } from '../utils/types';
 
-const getDRResources = (namespace: string) => ({
-  resources: {
-    drPolicies: getDRPolicyResourceObj(),
-    drClusters: getDRClusterResourceObj(),
-    drPlacementControls: getDRPlacementControlResourceObj({
-      namespace: namespace,
-    }),
-  },
-});
-
 const getApplicationSetResources = (
   appResource: ArgoApplicationSetKind,
   namespace: string,
   placementName: string,
   drResources: DisasterRecoveryResourceKind,
   drLoaded: boolean,
-  drLoadError: any
+  drLoadError: any,
+  isWatchApplication: boolean
 ) => ({
   resources: {
-    placements: getPlacementResourceObj({
-      name: placementName,
-      namespace: namespace,
-    }),
-    placementDecisions: getPlacementDecisionsResourceObj({
-      namespace: namespace,
-      selector: { matchLabels: { [PLACEMENT_REF_LABEL]: placementName } },
-    }),
+    ...(isWatchApplication
+      ? {
+          applications: getApplicationSetResourceObj({
+            name: getName(appResource),
+            namespace,
+          }),
+          placements: getPlacementResourceObj({
+            namespace: namespace,
+          }),
+          placementDecisions: getPlacementDecisionsResourceObj({
+            namespace: namespace,
+          }),
+        }
+      : {
+          placements: getPlacementResourceObj({
+            name: placementName,
+            namespace: namespace,
+          }),
+          placementDecisions: getPlacementDecisionsResourceObj({
+            namespace: namespace,
+            selector: { matchLabels: { [PLACEMENT_REF_LABEL]: placementName } },
+          }),
+        }),
   },
   drResources: {
     data: drResources,
@@ -68,11 +75,15 @@ const getApplicationSetResources = (
     loadError: drLoadError,
   },
   overrides: {
-    applications: {
-      data: appResource,
-      loaded: true,
-      loadError: '',
-    },
+    ...(!isWatchApplication
+      ? {
+          applications: {
+            data: appResource,
+            loaded: true,
+            loadError: '',
+          },
+        }
+      : {}),
     managedClusters: {
       data: {},
       loaded: true,
@@ -83,21 +94,24 @@ const getApplicationSetResources = (
 
 export const ApplicationSetParser: React.FC<ApplicationSetParserProps> = ({
   application,
-  isOpen,
-  close,
+  isWatchApplication,
+  pvcQueryFilter,
+  setCurrentModalContext,
 }) => {
+  const namespace = getNamespace(application);
   const [drResources, drLoaded, drLoadError] = useDisasterRecoveryResourceWatch(
-    getDRResources(getNamespace(application))
+    getDRResources(namespace)
   );
   const [appSetResources, loaded, loadError] =
     useArgoApplicationSetResourceWatch(
       getApplicationSetResources(
         application,
-        getNamespace(application),
+        namespace,
         findPlacementNameFromAppSet(application),
         drResources,
         drLoaded,
-        drLoadError
+        drLoadError,
+        isWatchApplication
       )
     );
 
@@ -117,25 +131,49 @@ export const ApplicationSetParser: React.FC<ApplicationSetParserProps> = ({
       const { placement, placementDecision, drPlacementControl, drPolicy } =
         appSetResource.placements[0];
 
+      const deploymentClusters = findDeploymentClusters(
+        placementDecision,
+        drPlacementControl
+      );
+
       const placementInfo = generatePlacementInfo(
         placement,
-        findDeploymentClusters(placementDecision, drPlacementControl)
+        deploymentClusters
       );
       const drpcInfo: DRPlacementControlType[] = generateDRPlacementControlInfo(
         drPlacementControl,
         placementInfo
       );
+
+      const remoteNamespace = getRemoteNamespaceFromAppSet(
+        appSetResource.application
+      );
+
       applicationInfo = generateApplicationInfo(
         DRApplication.APPSET,
-        application,
-        getRemoteNamespaceFromAppSet(application),
+        appSetResource.application,
+        remoteNamespace,
         // Skip placement if it already DR protected
         _.isEmpty(drpcInfo) ? [placementInfo] : [],
-        generateDRInfo(drPolicy, drpcInfo)
+        generateDRInfo(drPolicy, drpcInfo),
+        pvcQueryFilter || [
+          {
+            property: 'namespace',
+            values: remoteNamespace,
+          },
+          {
+            property: 'cluster',
+            values: deploymentClusters,
+          },
+          {
+            property: 'kind',
+            values: ['pod', 'deployment', 'replicaset'],
+          },
+        ]
       );
     }
     return applicationInfo;
-  }, [application, appSetResource, loaded, loadError]);
+  }, [appSetResource, loaded, loadError, pvcQueryFilter]);
 
   const matchingPolicies: DRPolicyType[] = React.useMemo(
     () =>
@@ -146,19 +184,24 @@ export const ApplicationSetParser: React.FC<ApplicationSetParserProps> = ({
   );
 
   return (
-    <AppManagePoliciesModal
-      applicationInfo={applicationInfo as ApplicationType}
+    <ModalContextViewer
+      applicationInfo={applicationInfo}
       matchingPolicies={matchingPolicies}
       loaded={loaded}
       loadError={loadError}
-      isOpen={isOpen}
-      close={close}
+      setCurrentModalContext={setCurrentModalContext}
     />
   );
 };
 
 type ApplicationSetParserProps = {
   application: ArgoApplicationSetKind;
-  isOpen: boolean;
-  close: () => void;
+  // Always watch the application resource
+  isWatchApplication?: boolean;
+  // Current active modal context
+  setCurrentModalContext: React.Dispatch<
+    React.SetStateAction<ModalViewContext>
+  >;
+  // ACM search api PVC query filter
+  pvcQueryFilter?: PVCQueryFilter;
 };
diff --git a/packages/mco/components/modals/app-manage-policies/parsers/index.ts b/packages/mco/components/modals/app-manage-policies/parsers/index.ts
new file mode 100644
index 000000000..0d0fc99ac
--- /dev/null
+++ b/packages/mco/components/modals/app-manage-policies/parsers/index.ts
@@ -0,0 +1,3 @@
+export * from './application-set-parser';
+export * from './subscription-parser';
+export * from './virtual-machine-parser';
diff --git a/packages/mco/components/modals/app-manage-policies/parsers/subscription-parser.spec.tsx b/packages/mco/components/modals/app-manage-policies/parsers/subscription-parser.spec.tsx
index 9e2087a55..4a5086734 100644
--- a/packages/mco/components/modals/app-manage-policies/parsers/subscription-parser.spec.tsx
+++ b/packages/mco/components/modals/app-manage-policies/parsers/subscription-parser.spec.tsx
@@ -32,7 +32,7 @@ import {
 } from '../../../../__mocks__/subscription';
 import { DisasterRecoveryResourceKind } from '../../../../hooks/disaster-recovery';
 import { SearchResult } from '../../../../types';
-import { SubscriptionParser } from './subscription-parser';
+import AppManagePoliciesModal from '../app-manage-policies-modal';
 
 let testCase = 1;
 
@@ -40,16 +40,20 @@ const searchResult: SearchResult = {
   data: {
     searchResult: [
       {
-        items: [
+        related: [
           {
-            apiversion: 'v1',
-            cluster: 'local-cluster',
-            created: '2023-07-04T17:14:10Z',
-            kind: 'PersistentVolumeClaim',
-            label: 'app=mock-application-2',
-            name: 'busybox-pvc',
-            namespace: 'test-ns',
-            _uid: 'local-cluster/683b0a87-85bf-4743-96d2-565863752e53',
+            items: [
+              {
+                apiversion: 'v1',
+                cluster: 'local-cluster',
+                created: '2023-07-04T17:14:10Z',
+                kind: 'PersistentVolumeClaim',
+                label: 'app=mock-application-2',
+                name: 'busybox-pvc',
+                namespace: 'test-ns',
+                _uid: 'local-cluster/683b0a87-85bf-4743-96d2-565863752e53',
+              },
+            ],
           },
         ],
       },
@@ -186,8 +190,8 @@ describe('Subscription manage disaster recovery modal', () => {
   test('Empty manage policy page test', async () => {
     testCase = 1;
     render(
-      <SubscriptionParser
-        application={mockApplication2}
+      <AppManagePoliciesModal
+        resource={mockApplication2}
         close={onClose}
         isOpen={true}
       />
@@ -208,8 +212,8 @@ describe('Subscription manage disaster recovery modal', () => {
   test('manage policy view test', async () => {
     testCase = 2;
     render(
-      <SubscriptionParser
-        application={mockApplication1}
+      <AppManagePoliciesModal
+        resource={mockApplication1}
         close={onClose}
         isOpen={true}
       />
@@ -239,8 +243,8 @@ describe('Subscription manage disaster recovery modal', () => {
     const user = userEvent.setup();
     testCase = 3;
     render(
-      <SubscriptionParser
-        application={mockApplication1}
+      <AppManagePoliciesModal
+        resource={mockApplication1}
         close={onClose}
         isOpen={true}
       />
diff --git a/packages/mco/components/modals/app-manage-policies/parsers/subscription-parser.tsx b/packages/mco/components/modals/app-manage-policies/parsers/subscription-parser.tsx
index 80c778dc9..39ab48bb9 100644
--- a/packages/mco/components/modals/app-manage-policies/parsers/subscription-parser.tsx
+++ b/packages/mco/components/modals/app-manage-policies/parsers/subscription-parser.tsx
@@ -1,57 +1,59 @@
 import * as React from 'react';
-import { DRApplication } from '@odf/mco/constants';
+import { DRApplication, HUB_CLUSTER_NAME } from '@odf/mco/constants';
 import {
   DisasterRecoveryResourceKind,
-  getDRClusterResourceObj,
-  getDRPlacementControlResourceObj,
-  getDRPolicyResourceObj,
   useSubscriptionResourceWatch,
   useDisasterRecoveryResourceWatch,
   getPlacementResourceObj,
   getPlacementDecisionsResourceObj,
   getSubscriptionResourceObj,
   getPlacementRuleResourceObj,
+  getApplicationResourceObj,
 } from '@odf/mco/hooks';
 import { DRPolicyKind } from '@odf/mco/types';
 import { findDeploymentClusters } from '@odf/mco/utils';
-import { getNamespace } from '@odf/shared/selectors';
+import { ApplicationModel, getName, getNamespace } from '@odf/shared';
 import { ApplicationKind } from '@odf/shared/types';
 import * as _ from 'lodash-es';
-import { AppManagePoliciesModal } from '../app-manage-policies-modal';
+import { ModalContextViewer } from '../modal-context-viewer';
 import {
   generateApplicationInfo,
   generateDRPlacementControlInfo,
   generateDRInfo,
   generatePlacementInfo,
   getMatchingDRPolicies,
+  getDRResources,
 } from '../utils/parser-utils';
+import { ModalViewContext } from '../utils/reducer';
 import {
   ApplicationInfoType,
   ApplicationType,
   DRPlacementControlType,
   DRPolicyType,
   PlacementType,
+  PVCQueryFilter,
 } from '../utils/types';
 
-const getDRResources = (namespace: string) => ({
-  resources: {
-    drPolicies: getDRPolicyResourceObj(),
-    drClusters: getDRClusterResourceObj(),
-    drPlacementControls: getDRPlacementControlResourceObj({
-      namespace,
-    }),
-  },
-});
-
 const getSubscriptionResources = (
   appResource: ApplicationKind,
   namespace: string,
   drResources: DisasterRecoveryResourceKind,
   drLoaded: boolean,
-  drLoadError: any
+  drLoadError: any,
+  subscriptionName: string,
+  isWatchApplication: boolean
 ) => ({
   resources: {
+    ...(isWatchApplication
+      ? {
+          applications: getApplicationResourceObj({
+            name: getName(appResource),
+            namespace,
+          }),
+        }
+      : {}),
     subscriptions: getSubscriptionResourceObj({
+      ...(!!subscriptionName ? { name: subscriptionName } : {}),
       namespace,
     }),
     placementRules: getPlacementRuleResourceObj({
@@ -70,38 +72,47 @@ const getSubscriptionResources = (
     loadError: drLoadError,
   },
   overrides: {
-    applications: {
-      data: appResource,
-      loaded: true,
-      loadError: '',
-    },
+    ...(!isWatchApplication
+      ? {
+          applications: {
+            data: appResource,
+            loaded: true,
+            loadError: '',
+          },
+        }
+      : {}),
   },
 });
 
 export const SubscriptionParser: React.FC<SubscriptionParserProps> = ({
   application,
-  isOpen,
-  close,
+  subscriptionName,
+  isWatchApplication,
+  pvcQueryFilter,
+  setCurrentModalContext,
 }) => {
+  const namespace = getNamespace(application);
   const [drResources, drLoaded, drLoadError] = useDisasterRecoveryResourceWatch(
-    getDRResources(getNamespace(application))
+    getDRResources(namespace)
   );
   const [subscriptionResourceList, loaded, loadError] =
     useSubscriptionResourceWatch(
       getSubscriptionResources(
         application,
-        getNamespace(application),
+        namespace,
         drResources,
         drLoaded,
-        drLoadError
+        drLoadError,
+        subscriptionName,
+        isWatchApplication
       )
     );
   const subscriptionResources = subscriptionResourceList?.[0];
   const { drPolicies } = drResources;
-
   const applicationInfo: ApplicationInfoType = React.useMemo(() => {
     let applicationInfo: ApplicationInfoType = {};
     if (loaded && !loadError) {
+      const app = subscriptionResources.application;
       const unProtectedPlacements: PlacementType[] = [];
       const drPlacementControls: DRPlacementControlType[] = [];
       let drPolicyInfo: DRPolicyKind = {};
@@ -134,14 +145,36 @@ export const SubscriptionParser: React.FC<SubscriptionParserProps> = ({
       );
       applicationInfo = generateApplicationInfo(
         DRApplication.SUBSCRIPTION,
-        application,
-        getNamespace(application),
+        app,
+        namespace,
         unProtectedPlacements,
-        generateDRInfo(drPolicyInfo, drPlacementControls)
+        generateDRInfo(drPolicyInfo, drPlacementControls),
+        pvcQueryFilter || [
+          {
+            property: 'name',
+            values: [getName(app)],
+          },
+          {
+            property: 'kind',
+            values: ApplicationModel.kind,
+          },
+          {
+            property: 'apigroup',
+            values: ApplicationModel.apiGroup,
+          },
+          {
+            property: 'namespace',
+            values: namespace,
+          },
+          {
+            property: 'cluster',
+            values: HUB_CLUSTER_NAME,
+          },
+        ]
       );
     }
     return applicationInfo;
-  }, [application, subscriptionResources, loaded, loadError]);
+  }, [subscriptionResources, namespace, loaded, loadError, pvcQueryFilter]);
 
   const matchingPolicies: DRPolicyType[] = React.useMemo(
     () =>
@@ -152,19 +185,27 @@ export const SubscriptionParser: React.FC<SubscriptionParserProps> = ({
   );
 
   return (
-    <AppManagePoliciesModal
-      applicationInfo={applicationInfo as ApplicationType}
+    <ModalContextViewer
+      applicationInfo={applicationInfo}
       matchingPolicies={matchingPolicies}
       loaded={loaded}
       loadError={loadError}
-      isOpen={isOpen}
-      close={close}
+      setCurrentModalContext={setCurrentModalContext}
     />
   );
 };
 
 type SubscriptionParserProps = {
+  // Application resource
   application: ApplicationKind;
-  isOpen: boolean;
-  close: () => void;
+  // Always watch the application resource
+  isWatchApplication?: boolean;
+  // Subscription name
+  subscriptionName?: string;
+  // Current active modal context
+  setCurrentModalContext: React.Dispatch<
+    React.SetStateAction<ModalViewContext>
+  >;
+  // ACM search api PVC query filter
+  pvcQueryFilter?: PVCQueryFilter;
 };
diff --git a/packages/mco/components/modals/app-manage-policies/parsers/virtual-machine-parser.tsx b/packages/mco/components/modals/app-manage-policies/parsers/virtual-machine-parser.tsx
new file mode 100644
index 000000000..d8d147406
--- /dev/null
+++ b/packages/mco/components/modals/app-manage-policies/parsers/virtual-machine-parser.tsx
@@ -0,0 +1,122 @@
+import * as React from 'react';
+import { HUB_CLUSTER_NAME, KUBE_INSTANCE_LABEL } from '@odf/mco/constants';
+import { useACMSafeFetch } from '@odf/mco/hooks';
+import {
+  ACMSubscriptionModel,
+  ArgoApplicationSetModel,
+  VirtualMachineModel,
+} from '@odf/mco/models';
+import { SearchResultItemType } from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import {
+  ApplicationModel,
+  getLabel,
+  getName,
+  getNamespace,
+  StatusBox,
+} from '@odf/shared';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
+import { ModalViewContext } from '../utils/reducer';
+import { PVCQueryFilter } from '../utils/types';
+import { ApplicationSetParser } from './application-set-parser';
+import { SubscriptionParser } from './subscription-parser';
+
+const getPVCQueryFilter = (
+  name: string,
+  namespace: string,
+  cluster: string
+): PVCQueryFilter => [
+  { property: 'name', values: name },
+  { property: 'kind', values: VirtualMachineModel.kind },
+  { property: 'apigroup', values: VirtualMachineModel.apiGroup },
+  { property: 'namespace', values: namespace },
+  { property: 'cluster', values: cluster },
+];
+
+export const VirtualMachineParser: React.FC<VirtualMachineParserProps> = ({
+  application: virtualMachine,
+  cluster,
+  setCurrentModalContext,
+}) => {
+  const vmName = getName(virtualMachine);
+  const vmNamespace = getNamespace(virtualMachine);
+  const clusterName = virtualMachine?.['status']?.cluster || cluster;
+  const applicationSetName = getLabel(virtualMachine, KUBE_INSTANCE_LABEL);
+  const pvcQueryFilter = getPVCQueryFilter(vmName, vmNamespace, clusterName);
+
+  // ACM search API call to find managed application resource
+  const searchQuery = React.useMemo(
+    () =>
+      queryManagedApplicationResourcesForVM(
+        [vmName, applicationSetName],
+        vmNamespace,
+        clusterName
+      ),
+    [vmName, vmNamespace, clusterName, applicationSetName]
+  );
+
+  const [searchResult, searchError, searchLoaded] =
+    useACMSafeFetch(searchQuery);
+
+  if (!searchLoaded || searchError) {
+    return <StatusBox loaded={searchLoaded} loadError={searchError} />;
+  }
+
+  // Extract managed application CR (Application / ApplicationSet)
+  const managedApplication: SearchResultItemType =
+    searchResult?.data?.searchResult?.[0]?.related?.[0]?.items?.find(
+      (item) => item.cluster === HUB_CLUSTER_NAME
+    );
+
+  const { name, namespace, kind, apigroup } = managedApplication || {};
+
+  if (kind === ArgoApplicationSetModel.kind) {
+    return (
+      <ApplicationSetParser
+        application={{
+          apiVersion: `${ArgoApplicationSetModel.apiGroup}/${ArgoApplicationSetModel.apiVersion}`,
+          kind,
+          metadata: { name, namespace },
+          spec: {},
+        }}
+        setCurrentModalContext={setCurrentModalContext}
+        isWatchApplication
+        pvcQueryFilter={pvcQueryFilter}
+      />
+    );
+  }
+
+  if (kind === ACMSubscriptionModel.kind) {
+    return (
+      <SubscriptionParser
+        application={{
+          apiVersion: `${ApplicationModel.apiGroup}/${ApplicationModel.apiVersion}`,
+          kind: ApplicationModel.kind,
+          metadata: {
+            name: getLabelsFromSearchResult(managedApplication)?.app?.[0],
+            namespace,
+          },
+          spec: { componentKinds: [{ group: apigroup, kind }] },
+        }}
+        subscriptionName={name}
+        isWatchApplication
+        setCurrentModalContext={setCurrentModalContext}
+        pvcQueryFilter={pvcQueryFilter}
+      />
+    );
+  }
+
+  // TODO: Add support for discovered application type
+  return null;
+};
+
+type VirtualMachineParserProps = {
+  application: K8sResourceCommon;
+  cluster?: string;
+  setCurrentModalContext: React.Dispatch<
+    React.SetStateAction<ModalViewContext>
+  >;
+};
diff --git a/packages/mco/components/modals/app-manage-policies/utils/parser-utils.ts b/packages/mco/components/modals/app-manage-policies/utils/parser-utils.ts
index 911f6a82b..06a2dfd4e 100644
--- a/packages/mco/components/modals/app-manage-policies/utils/parser-utils.ts
+++ b/packages/mco/components/modals/app-manage-policies/utils/parser-utils.ts
@@ -1,4 +1,9 @@
 import { DRApplication } from '@odf/mco/constants';
+import {
+  getDRClusterResourceObj,
+  getDRPlacementControlResourceObj,
+  getDRPolicyResourceObj,
+} from '@odf/mco/hooks';
 import {
   ACMApplicationKind,
   ACMPlacementType,
@@ -20,6 +25,7 @@ import {
   DRPlacementControlType,
   DRInfoType,
   PlacementType,
+  PVCQueryFilter,
 } from './types';
 
 const getPVCSelector = (pvcSelector: Selector): string[] => {
@@ -90,7 +96,8 @@ export const generateApplicationInfo = (
   application: ACMApplicationKind,
   workloadNamespace: string,
   plsInfo: PlacementType[],
-  drInfo: DRInfoType | {}
+  drInfo: DRInfoType | {},
+  pvcQueryFilter: PVCQueryFilter
 ): ApplicationType => ({
   type: appType,
   apiVersion: application.apiVersion,
@@ -99,6 +106,7 @@ export const generateApplicationInfo = (
   workloadNamespace: workloadNamespace,
   placements: plsInfo,
   drInfo: drInfo,
+  pvcQueryFilter: pvcQueryFilter,
 });
 
 export const getClusterNamesFromPlacements = (placements: PlacementType[]) =>
@@ -124,3 +132,13 @@ export const getMatchingDRPolicies = (
     }, []) || []
   );
 };
+
+export const getDRResources = (namespace: string) => ({
+  resources: {
+    drPolicies: getDRPolicyResourceObj(),
+    drClusters: getDRClusterResourceObj(),
+    drPlacementControls: getDRPlacementControlResourceObj({
+      namespace,
+    }),
+  },
+});
diff --git a/packages/mco/components/modals/app-manage-policies/utils/types.ts b/packages/mco/components/modals/app-manage-policies/utils/types.ts
index 8f00a5c48..67ffe6f8c 100644
--- a/packages/mco/components/modals/app-manage-policies/utils/types.ts
+++ b/packages/mco/components/modals/app-manage-policies/utils/types.ts
@@ -39,6 +39,8 @@ export type DRInfoType = {
   placementControlInfo?: DRPlacementControlType[];
 };
 
+export type PVCQueryFilter = { property: string; values: string[] | string }[];
+
 export type ApplicationType = K8sResourceCommon & {
   // ACM managed application types
   type: DRApplication;
@@ -48,6 +50,8 @@ export type ApplicationType = K8sResourceCommon & {
   placements: PlacementType[];
   // Disaster recovery info
   drInfo?: DRInfoType | {};
+  // ACM search API filter to read application PVCs
+  pvcQueryFilter: PVCQueryFilter;
 };
 
 export type ApplicationInfoType = ApplicationType | {};
diff --git a/packages/mco/constants/acm.ts b/packages/mco/constants/acm.ts
index 6b039f3f2..757f48ea3 100644
--- a/packages/mco/constants/acm.ts
+++ b/packages/mco/constants/acm.ts
@@ -45,7 +45,8 @@ export const MANAGED_CLUSTER_JOINED = 'ManagedClusterJoined';
 export const LABEL = 'label';
 export const LABELS_SPLIT_CHAR = '; ';
 export const LABEL_SPLIT_CHAR = '=';
-export const DR_BLOCK_LISTED_LABELS = ['app.kubernetes.io/instance'];
+export const KUBE_INSTANCE_LABEL = 'app.kubernetes.io/instance';
+export const DR_BLOCK_LISTED_LABELS = [KUBE_INSTANCE_LABEL];
 
 export const ACM_OPERATOR_SPEC_NAME = 'advanced-cluster-management';
 
diff --git a/packages/mco/hooks/subscription.ts b/packages/mco/hooks/subscription.ts
index 7ebda08ec..2d0419c8a 100644
--- a/packages/mco/hooks/subscription.ts
+++ b/packages/mco/hooks/subscription.ts
@@ -247,10 +247,13 @@ export const useSubscriptionResourceWatch: UseSubscriptionResourceWatch = (
       const applicationList = Array.isArray(applications)
         ? applications
         : [applications];
+      const subscriptionsList = Array.isArray(subscriptions)
+        ? subscriptions
+        : [subscriptions];
       const namespaceToApplicationMap =
         getNamespaceWiseApplications(applicationList);
       const namespaceToSubscriptionMap =
-        getNamespaceWiseSubscriptions(subscriptions);
+        getNamespaceWiseSubscriptions(subscriptionsList);
       const namespaceToPlacementRuleMap =
         getNamespaceWisePlacements(placementRules);
       const namespaceToPlacementMap = getNamespaceWisePlacements(placements);
diff --git a/packages/mco/models/index.ts b/packages/mco/models/index.ts
index 8715f7e7d..bea17bd81 100644
--- a/packages/mco/models/index.ts
+++ b/packages/mco/models/index.ts
@@ -2,3 +2,4 @@ export * from './acm';
 export * from './odf-mco';
 export * from './ramen';
 export * from './argo-cd';
+export * from './kubevirt';
diff --git a/packages/mco/models/kubevirt.ts b/packages/mco/models/kubevirt.ts
new file mode 100644
index 000000000..28a3d1898
--- /dev/null
+++ b/packages/mco/models/kubevirt.ts
@@ -0,0 +1,13 @@
+import { K8sModel } from '@openshift-console/dynamic-plugin-sdk/lib/api/common-types';
+
+export const VirtualMachineModel: K8sModel = {
+  label: 'VirtualMachine',
+  labelPlural: 'VirtualMachines',
+  apiVersion: 'v1',
+  apiGroup: 'kubevirt.io',
+  plural: 'virtualmachines',
+  abbr: 'VM',
+  namespaced: true,
+  kind: 'VirtualMachine',
+  crd: true,
+};
diff --git a/packages/mco/types/acm.ts b/packages/mco/types/acm.ts
index 78f833392..5cfecadb0 100644
--- a/packages/mco/types/acm.ts
+++ b/packages/mco/types/acm.ts
@@ -159,8 +159,7 @@ export type SearchResult = {
       items?: SearchResultItemType[];
       count?: number;
       related?: {
-        count: number;
-        kind: string;
+        items?: SearchResultItemType[];
       }[];
     }[];
   };
diff --git a/packages/mco/utils/acm-search-queries.ts b/packages/mco/utils/acm-search-queries.ts
index 267c243d7..3a33a075d 100644
--- a/packages/mco/utils/acm-search-queries.ts
+++ b/packages/mco/utils/acm-search-queries.ts
@@ -3,7 +3,18 @@ import {
   K8sResourceCommon,
   ObjectMetadata,
 } from '@openshift-console/dynamic-plugin-sdk';
-import { LABELS_SPLIT_CHAR, LABEL_SPLIT_CHAR } from '../constants';
+import { PVCQueryFilter } from '../components/modals/app-manage-policies/utils/types';
+import {
+  GITOPS_OPERATOR_NAMESPACE,
+  HUB_CLUSTER_NAME,
+  LABELS_SPLIT_CHAR,
+  LABEL_SPLIT_CHAR,
+} from '../constants';
+import {
+  ACMSubscriptionModel,
+  ArgoApplicationSetModel,
+  VirtualMachineModel,
+} from '../models';
 
 // Search query
 export const searchFilterQuery =
@@ -13,32 +24,19 @@ export const searchRelatedItemsFilterQuery =
   'query searchResultRelatedItems($input: [SearchInput]) {\n  searchResult: search(input: $input) {\n    items\n    related {\n      kind\n      items\n      __typename\n    }\n    __typename\n  }\n}';
 
 export const queryAppWorkloadPVCs = (
-  workloadNamespace: string,
-  clusterNames: string[]
+  pvcQueryFilter: PVCQueryFilter
 ): SearchQuery => ({
-  operationName: 'searchResult',
+  operationName: 'searchResultRelatedItems',
   variables: {
     input: [
       {
-        filters: [
-          {
-            property: 'kind',
-            values: ['persistentvolumeclaim'],
-          },
-          {
-            property: 'namespace',
-            values: [workloadNamespace],
-          },
-          {
-            property: 'cluster',
-            values: clusterNames,
-          },
-        ],
-        limit: 20, // search said not to use unlimited results
+        filters: pvcQueryFilter,
+        relatedKinds: ['persistentvolumeclaim'],
+        limit: 100, // search said not to use unlimited results
       },
     ],
   },
-  query: searchFilterQuery,
+  query: searchRelatedItemsFilterQuery,
 });
 
 export const queryNamespacesUsingCluster = (
@@ -65,37 +63,40 @@ export const queryNamespacesUsingCluster = (
   query: searchFilterQuery,
 });
 
+export const convertSearchResult = (
+  result: SearchResultItemType
+): K8sResourceCommon => {
+  const resourceName = result.name;
+  // example labels: "key1:value1;key2=value2"
+  const labelList: string[] = result.label?.split(LABELS_SPLIT_CHAR) || [];
+  const labels: ObjectMetadata['labels'] = labelList.reduce((acc, label) => {
+    const [key, value] = label.split(LABEL_SPLIT_CHAR);
+    acc[key] = value;
+    return acc;
+  }, {});
+  return {
+    apiVersion: result.apigroup
+      ? `${result.apigroup}/${result.apiversion}`
+      : result.apiversion,
+    kind: result.kind,
+    metadata: {
+      name: resourceName,
+      namespace: result.namespace,
+      creationTimestamp: result.created,
+      uid: result._uid, // managedClusterName/uuid
+      labels,
+    },
+    status: {
+      cluster: result.cluster,
+      resourceName,
+    },
+  } as K8sResourceCommon;
+};
+
 // Only some metadata information are coverted
 export const convertSearchResultToK8sResourceCommon = (
   searchResultItems: SearchResultItemType[]
-): K8sResourceCommon[] =>
-  searchResultItems.map((item) => {
-    const resourceName = item.name;
-    // example labels: "key1:value1;key2=value2"
-    const labelList: string[] = item.label?.split(LABELS_SPLIT_CHAR) || [];
-    const labels: ObjectMetadata['labels'] = labelList.reduce((acc, label) => {
-      const [key, value] = label.split(LABEL_SPLIT_CHAR);
-      acc[key] = value;
-      return acc;
-    }, {});
-    return {
-      apiVersion: item.apigroup
-        ? `${item.apigroup}/${item.apiversion}`
-        : item.apiversion,
-      kind: item.kind,
-      metadata: {
-        name: resourceName,
-        namespace: item.namespace,
-        creationTimestamp: item.created,
-        uid: item._uid, // managedClusterName/uuid
-        labels,
-      },
-      status: {
-        cluster: item.cluster,
-        resourceName,
-      },
-    };
-  });
+): K8sResourceCommon[] => searchResultItems.map(convertSearchResult);
 
 export const queryRecipesFromCluster = (
   clusterName: string,
@@ -155,3 +156,46 @@ export const queryK8sResourceFromCluster = (
   },
   query: searchRelatedItemsFilterQuery,
 });
+
+// ACM seach query to find managed application resources of the VM.
+export const queryManagedApplicationResourcesForVM = (
+  names: string[],
+  namespace: string,
+  cluster: string
+): SearchQuery => ({
+  operationName: 'searchResultRelatedItems',
+  variables: {
+    input: [
+      {
+        filters: [
+          {
+            property: 'namespace',
+            values: [namespace, GITOPS_OPERATOR_NAMESPACE],
+          },
+          {
+            property: 'cluster',
+            values: [cluster, HUB_CLUSTER_NAME],
+          },
+          {
+            property: 'kind',
+            values: [VirtualMachineModel.kind, 'Application'],
+          },
+          {
+            property: 'name',
+            values: names.filter(Boolean),
+          },
+          {
+            property: 'apigroup',
+            values: [
+              VirtualMachineModel.apiGroup,
+              ArgoApplicationSetModel.apiGroup,
+            ],
+          },
+        ],
+        relatedKinds: [ACMSubscriptionModel.kind, ArgoApplicationSetModel.kind],
+        limit: 2, // search said not to use unlimited results
+      },
+    ],
+  },
+  query: searchRelatedItemsFilterQuery,
+});
diff --git a/packages/mco/utils/disaster-recovery.tsx b/packages/mco/utils/disaster-recovery.tsx
index 7441662ca..d51ea335f 100644
--- a/packages/mco/utils/disaster-recovery.tsx
+++ b/packages/mco/utils/disaster-recovery.tsx
@@ -653,13 +653,14 @@ export const getNameNamespace = (name: string, namespace: string) =>
     : '';
 
 export const getLabelsFromSearchResult = (
-  item: SearchResultItemType
+  item: SearchResultItemType,
+  isAllowBlocklist: boolean = false
 ): { [key in string]: string[] } => {
   // example label foo1=bar1;foo2=bar2
   const labels: string[] = item?.label?.split(LABELS_SPLIT_CHAR) || [];
   return labels?.reduce((acc, label) => {
     const [key, value] = label.split(LABEL_SPLIT_CHAR);
-    if (!DR_BLOCK_LISTED_LABELS.includes(key)) {
+    if (isAllowBlocklist || !DR_BLOCK_LISTED_LABELS.includes(key)) {
       acc[key] = [...(acc[key] || []), value];
     }
     return acc;
diff --git a/plugins/mco/console-extensions.json b/plugins/mco/console-extensions.json
index 28e911242..846ea38ee 100644
--- a/plugins/mco/console-extensions.json
+++ b/plugins/mco/console-extensions.json
@@ -156,5 +156,21 @@
       "exact": true,
       "component": { "$codeRef": "enrollDiscoveredApplication.default" }
     }
+  },
+  {
+    "type": "acm.virtualmachine/action",
+    "properties": {
+      "id": "appManagePolicy",
+      "title": "%plugin__odf-multicluster-console~Manage disaster recovery%",
+      "model": [
+        {
+          "apiVersion": "kubevirt.io/v1",
+          "kind": "VirtualMachine"
+        }
+      ],
+      "component": {
+        "$codeRef": "appManagePolicy.default"
+      }
+    }
   }
 ]
diff --git a/plugins/mco/console-plugin.json b/plugins/mco/console-plugin.json
index 5b2ced75d..c4e7ca96c 100644
--- a/plugins/mco/console-plugin.json
+++ b/plugins/mco/console-plugin.json
@@ -14,7 +14,7 @@
     "editPage": "@odf/core/components/ResourceEditor/GenericResourceEditor",
     "dataPolicyStatusPopover": "@odf/mco/components/modals/app-data-policies-status/acm-column-callback",
     "appFailoverRelocate": "@odf/mco/components/modals/app-failover-relocate/acm-action-callback",
-    "appManagePolicy": "@odf/mco/components/modals/app-manage-policies/acm-action-callback",
+    "appManagePolicy": "@odf/mco/components/modals/app-manage-policies/app-manage-policies-modal",
     "enrollDiscoveredApplication": "@odf/mco/components/discovered-application-wizard/enroll-discovered-application"
   }
 }


Reviews:
user: login: SanjalKatiyar
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-19T13:34:50Z
user: login: alfonsomthd
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-25T11:04:15Z
user: login: alfonsomthd
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-25T11:04:47Z
user: login: alfonsomthd
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-25T11:05:25Z
user: login: bipuladh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-25T11:09:38Z
user: login: alfonsomthd
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-25T11:11:11Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T14:56:31Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T15:05:18Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T15:59:29Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T16:05:02Z
user: login: SanjalKatiyar
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T14:22:34Z
user: login: alfonsomthd
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T14:38:57Z
user: login: alfonsomthd
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T14:41:29Z
user: login: alfonsomthd
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-11T13:28:48Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T13:31:21Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T13:31:39Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T13:32:15Z

Review comments:
diff_hunk: @@ -0,0 +1,51 @@
+import * as React from 'react';
+import { ArgoApplicationSetModel, VirtualMachineModel } from '@odf/mco/models';
+import { ApplicationModel } from '@odf/shared/models';
+import {
+  getGVKofResource,
+  referenceFor,
+  referenceForModel,
+} from '@odf/shared/utils';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  VirtualMachineParser,
+} from './parsers';
+import { ModalViewContext } from './utils/reducer';
+
+const getGVK = (resource: K8sResourceCommon) =>
+  resource?.['apigroup']
+    ? // ACM search API compatibile fields
path: packages/mco/components/modals/app-manage-policies/app-manage-policies-modal-body.tsx
user: login: SanjalKatiyar
body: ```suggestion
    ? // ACM search API compatible fields
```
created_at: 2025-02-19T13:09:00Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,13 @@
+import { K8sModel } from '@openshift-console/dynamic-plugin-sdk/lib/api/common-types';
+
+export const VirtualMachineModel: K8sModel = {
+  label: 'VirtualMachine',
+  labelPlural: 'VirtualMachines',
+  apiVersion: 'v1',
+  apiGroup: 'kubevirt.io',
+  plural: 'virtualmachines',
+  abbr: 'VM',
+  namespaced: true,
+  kind: 'VirtualMachine',
+  crd: true,
+};
path: packages/mco/models/kubevirt.ts
user: login: SanjalKatiyar
body: is VM a new CRD, but in ACM it's represented as either AppSet or Subscription ??
created_at: 2025-02-19T13:10:58Z
author_association: COLLABORATOR
diff_hunk: @@ -1,144 +1,64 @@
 import * as React from 'react';
-import { DRApplication } from '@odf/mco/constants';
 import { getName, getNamespace } from '@odf/shared/selectors';
 import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
 import { Trans } from 'react-i18next';
 import { Modal, ModalVariant } from '@patternfly/react-core';
-import { AssignPolicyView } from './assign-policy-view';
-import { ManagePolicyView } from './manage-policy-view';
-import {
-  ManagePolicyStateAction,
-  ManagePolicyStateType,
-  ModalActionContext,
-  ModalViewContext,
-  initialPolicyState,
-  managePolicyStateReducer,
-  ManagePolicyState,
-} from './utils/reducer';
-import { ApplicationType, DRInfoType, DRPolicyType } from './utils/types';
+import { AppManagePoliciesModalBody } from './app-manage-policies-modal-body';
+import { ModalViewContext } from './utils/reducer';
 
-export const ModalContextViewer: React.FC<ModalContextViewerProps> = ({
-  applicaitonInfo,
-  state,
-  matchingPolicies,
-  dispatch,
-  loaded,
-  loadError,
+export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
+  resource: application,
+  cluster,
+  isOpen,
+  close,
 }) => {
-  const setModalContext = React.useCallback(
-    (modalViewContext: ModalViewContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
-        payload: modalViewContext,
-      }),
-    [dispatch]
+  const { t } = useCustomTranslation();
+  const [currentModalContext, setCurrentModalContext] = React.useState(
+    ModalViewContext.MANAGE_POLICY_VIEW
   );
 
-  const setModalActionContext = React.useCallback(
-    (modalActionContext: ModalActionContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
-        payload: modalActionContext,
-      }),
-    [dispatch]
-  );
+  const applicationName = getName(application) ?? application?.['name'];
+  const applicationNamespace =
+    getNamespace(application) ?? application?.['namespace'];
 
-  return (
-    <>
-      {state.modalViewContext === ModalViewContext.MANAGE_POLICY_VIEW && (
-        <ManagePolicyView
-          drInfo={applicaitonInfo?.drInfo as DRInfoType}
-          workloadNamespace={applicaitonInfo?.workloadNamespace}
-          eligiblePolicies={matchingPolicies}
-          isSubscriptionAppType={
-            applicaitonInfo?.type === DRApplication.SUBSCRIPTION
-          }
-          unProtectedPlacementCount={applicaitonInfo?.placements?.length}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-          loaded={loaded}
-          loadError={loadError}
-          modalActionContext={state.modalActionContext}
-        />
-      )}
-      {state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW && (
-        <AssignPolicyView
-          applicaitonInfo={applicaitonInfo}
-          matchingPolicies={matchingPolicies}
-          state={state.assignPolicyView}
-          modalActionContext={state.modalActionContext}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-        />
-      )}
-    </>
-  );
-};
+  const title =
+    currentModalContext === ModalViewContext.ASSIGN_POLICY_VIEW
+      ? t('Enroll managed application')
+      : t('Manage disaster recovery');
 
-export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
-  applicaitonInfo,
-  matchingPolicies,
-  loaded,
-  loadError,
-  isOpen,
-  close,
-}) => {
-  const [state, dispatch] = React.useReducer(
-    managePolicyStateReducer,
-    initialPolicyState
+  const description = (
+    <Trans t={t}>
+      <strong>Application:</strong> {applicationName} (Namespace:{' '}
+      {applicationNamespace})
+    </Trans>
   );
-  const { t } = useCustomTranslation();
 
   return (
     <Modal
-      title={
-        state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW
-          ? t('Enroll managed application')
-          : t('Manage disaster recovery')
-      }
-      description={
-        loaded &&
-        !loadError && (
-          <Trans t={t}>
-            <strong>Application:</strong> {getName(applicaitonInfo)} (Namespace:{' '}
-            {getNamespace(applicaitonInfo)})
-          </Trans>
-        )
-      }
+      title={title}
+      description={description}
       variant={ModalVariant.large}
       isOpen={isOpen}
       aria-label="Manage policy modal"
       aria-describedby="manage-policy-modal"
       onClose={close}
     >
-      <ModalContextViewer
-        applicaitonInfo={applicaitonInfo}
-        matchingPolicies={matchingPolicies}
-        state={state}
-        dispatch={dispatch}
-        loaded={loaded}
-        loadError={loadError}
+      <AppManagePoliciesModalBody
+        application={application}
+        cluster={cluster}
+        setCurrentModalContext={setCurrentModalContext}
       />
     </Modal>
   );
 };
 
 type AppManagePoliciesModalProps = {
-  applicaitonInfo: ApplicationType;
-  matchingPolicies: DRPolicyType[];
-  loaded: boolean;
-  loadError: any;
+  resource: K8sResourceCommon;
   isOpen: boolean;
-  close?: () => void;
+  close: () => void;
+  // ACM search API compatibile field
path: packages/mco/components/modals/app-manage-policies/app-manage-policies-modal.tsx
user: login: SanjalKatiyar
body: ```suggestion
  // ACM search API compatible field
```
created_at: 2025-02-19T13:13:11Z
author_association: COLLABORATOR
diff_hunk: @@ -61,14 +60,17 @@ const getPlacementTags = (pvcSelectors: PVCSelectorType[]): TagsType =>
 
 const getLabelsFromSearchResult = (searchResult: SearchResult): string[] => {
   const pvcLabels =
-    searchResult?.data.searchResult?.[0]?.items.reduce((acc, item) => {
-      const labels: string[] = item[LABEL]?.split(LABELS_SPLIT_CHAR) || [];
-      const filteredLabels = labels?.filter((label) => {
-        const [key] = label.split(LABEL_SPLIT_CHAR);
-        return !DR_BLOCK_LISTED_LABELS.includes(key);
-      });
-      return [...acc, ...filteredLabels];
-    }, []) || [];
+    searchResult?.data.searchResult?.[0]?.related?.[0]?.items?.reduce(
path: packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx
user: login: SanjalKatiyar
body: is this `related` change backward compatible with older version of ACM (as per our support matrix) ??

Meaning, say ODF 4.19 is running on older ACM version which is supported as per our support matrix, but that ACM version do not add/support `related` to the resultant object.
created_at: 2025-02-19T13:27:16Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,51 @@
+import * as React from 'react';
+import { ArgoApplicationSetModel, VirtualMachineModel } from '@odf/mco/models';
+import { ApplicationModel } from '@odf/shared/models';
+import {
+  getGVKofResource,
+  referenceFor,
+  referenceForModel,
+} from '@odf/shared/utils';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  VirtualMachineParser,
+} from './parsers';
+import { ModalViewContext } from './utils/reducer';
+
+const getGVK = (resource: K8sResourceCommon) =>
+  resource?.['apigroup']
path: packages/mco/components/modals/app-manage-policies/app-manage-policies-modal-body.tsx
user: login: bipuladh
body: I am not sure what you are trying to do here. `K8sResourceCommon` does not have a `apiGroup` field. And are you using object notation to skip ts checks? 
created_at: 2025-02-25T10:36:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+import * as React from 'react';
+import { ArgoApplicationSetModel, VirtualMachineModel } from '@odf/mco/models';
+import { ApplicationModel } from '@odf/shared/models';
+import {
+  getGVKofResource,
+  referenceFor,
+  referenceForModel,
+} from '@odf/shared/utils';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  VirtualMachineParser,
+} from './parsers';
+import { ModalViewContext } from './utils/reducer';
+
+const getGVK = (resource: K8sResourceCommon) =>
+  resource?.['apigroup']
+    ? // ACM search API compatibile fields
+      referenceFor(resource['apigroup'])(resource['apiversion'])(resource.kind)
path: packages/mco/components/modals/app-manage-policies/app-manage-policies-modal-body.tsx
user: login: bipuladh
body: The usage of this is wrong if the input object is of type `K8sResourceCommon`. `referenceFor` will give you a GVK literal with a `/`. 
created_at: 2025-02-25T10:38:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,100 @@
+import * as React from 'react';
+import { DRApplication } from '@odf/mco/constants';
+import { AssignPolicyView } from './assign-policy-view';
+import { ManagePolicyView } from './manage-policy-view';
+import {
+  initialPolicyState,
+  managePolicyStateReducer,
+  ManagePolicyStateType,
+  ModalActionContext,
+  ModalViewContext,
+} from './utils/reducer';
+import {
+  ApplicationInfoType,
+  ApplicationType,
+  DRInfoType,
+  DRPolicyType,
+} from './utils/types';
+
+export const ModalContextViewer: React.FC<ModalContextViewerProps> = ({
+  applicationInfo,
+  matchingPolicies,
+  loaded,
+  loadError,
+  setCurrentModalContext,
+}) => {
+  const [state, dispatch] = React.useReducer(
+    managePolicyStateReducer,
+    initialPolicyState
+  );
+
+  const setModalContext = React.useCallback(
+    (modalViewContext: ModalViewContext) => {
+      dispatch({
+        type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
+        payload: modalViewContext,
+      });
+      setCurrentModalContext(modalViewContext);
+    },
+    [setCurrentModalContext]
+  );
+
+  const setModalActionContext = React.useCallback(
+    (modalActionContext: ModalActionContext) =>
+      dispatch({
+        type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
+        payload: modalActionContext,
+      }),
+    []
+  );
+
+  const applicaiton = applicationInfo as ApplicationType;
path: packages/mco/components/modals/app-manage-policies/modal-context-viewer.tsx
user: login: alfonsomthd
body: ```suggestion
  const application = applicationInfo as ApplicationType;
```
created_at: 2025-02-25T11:04:14Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,100 @@
+import * as React from 'react';
+import { DRApplication } from '@odf/mco/constants';
+import { AssignPolicyView } from './assign-policy-view';
+import { ManagePolicyView } from './manage-policy-view';
+import {
+  initialPolicyState,
+  managePolicyStateReducer,
+  ManagePolicyStateType,
+  ModalActionContext,
+  ModalViewContext,
+} from './utils/reducer';
+import {
+  ApplicationInfoType,
+  ApplicationType,
+  DRInfoType,
+  DRPolicyType,
+} from './utils/types';
+
+export const ModalContextViewer: React.FC<ModalContextViewerProps> = ({
+  applicationInfo,
+  matchingPolicies,
+  loaded,
+  loadError,
+  setCurrentModalContext,
+}) => {
+  const [state, dispatch] = React.useReducer(
+    managePolicyStateReducer,
+    initialPolicyState
+  );
+
+  const setModalContext = React.useCallback(
+    (modalViewContext: ModalViewContext) => {
+      dispatch({
+        type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
+        payload: modalViewContext,
+      });
+      setCurrentModalContext(modalViewContext);
+    },
+    [setCurrentModalContext]
+  );
+
+  const setModalActionContext = React.useCallback(
+    (modalActionContext: ModalActionContext) =>
+      dispatch({
+        type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
+        payload: modalActionContext,
+      }),
+    []
+  );
+
+  const applicaiton = applicationInfo as ApplicationType;
+
+  const renderView = () => {
+    if (state.modalViewContext === ModalViewContext.MANAGE_POLICY_VIEW) {
+      return (
+        <ManagePolicyView
+          drInfo={applicaiton?.drInfo as DRInfoType}
+          workloadNamespace={applicaiton?.workloadNamespace}
+          eligiblePolicies={matchingPolicies}
+          isSubscriptionAppType={
+            applicaiton?.type === DRApplication.SUBSCRIPTION
+          }
+          unProtectedPlacementCount={applicaiton?.placements?.length}
+          dispatch={dispatch}
+          setModalContext={setModalContext}
+          setModalActionContext={setModalActionContext}
+          loaded={loaded}
+          loadError={loadError}
+          modalActionContext={state.modalActionContext}
+        />
+      );
+    }
+    if (state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW) {
+      return (
+        <AssignPolicyView
+          applicaitonInfo={applicaiton}
path: packages/mco/components/modals/app-manage-policies/modal-context-viewer.tsx
user: login: alfonsomthd
body: ```suggestion
          applicationInfo={application}
```
created_at: 2025-02-25T11:04:46Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,100 @@
+import * as React from 'react';
+import { DRApplication } from '@odf/mco/constants';
+import { AssignPolicyView } from './assign-policy-view';
+import { ManagePolicyView } from './manage-policy-view';
+import {
+  initialPolicyState,
+  managePolicyStateReducer,
+  ManagePolicyStateType,
+  ModalActionContext,
+  ModalViewContext,
+} from './utils/reducer';
+import {
+  ApplicationInfoType,
+  ApplicationType,
+  DRInfoType,
+  DRPolicyType,
+} from './utils/types';
+
+export const ModalContextViewer: React.FC<ModalContextViewerProps> = ({
+  applicationInfo,
+  matchingPolicies,
+  loaded,
+  loadError,
+  setCurrentModalContext,
+}) => {
+  const [state, dispatch] = React.useReducer(
+    managePolicyStateReducer,
+    initialPolicyState
+  );
+
+  const setModalContext = React.useCallback(
+    (modalViewContext: ModalViewContext) => {
+      dispatch({
+        type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
+        payload: modalViewContext,
+      });
+      setCurrentModalContext(modalViewContext);
+    },
+    [setCurrentModalContext]
+  );
+
+  const setModalActionContext = React.useCallback(
+    (modalActionContext: ModalActionContext) =>
+      dispatch({
+        type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
+        payload: modalActionContext,
+      }),
+    []
+  );
+
+  const applicaiton = applicationInfo as ApplicationType;
+
+  const renderView = () => {
+    if (state.modalViewContext === ModalViewContext.MANAGE_POLICY_VIEW) {
+      return (
+        <ManagePolicyView
+          drInfo={applicaiton?.drInfo as DRInfoType}
+          workloadNamespace={applicaiton?.workloadNamespace}
+          eligiblePolicies={matchingPolicies}
+          isSubscriptionAppType={
+            applicaiton?.type === DRApplication.SUBSCRIPTION
+          }
+          unProtectedPlacementCount={applicaiton?.placements?.length}
path: packages/mco/components/modals/app-manage-policies/modal-context-viewer.tsx
user: login: alfonsomthd
body: ```suggestion
          unprotectedPlacementCount={application?.placements?.length}
```
created_at: 2025-02-25T11:05:25Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,142 @@
+import * as React from 'react';
+import { KUBE_INSTANCE_LABEL } from '@odf/mco/constants';
+import { useACMSafeFetch } from '@odf/mco/hooks';
+import {
+  ACMSubscriptionModel,
+  ArgoApplicationSetModel,
+  VirtualMachineModel,
+} from '@odf/mco/models';
+import { SearchResultItemType, VirtualMachineKind } from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import { ApplicationModel, getLabel, getName, getNamespace } from '@odf/shared';
+import { ModalContextViewer } from '../modal-context-viewer';
+import { ModalViewContext } from '../utils/reducer';
+import { PVCQueryFilter } from '../utils/types';
+import { ApplicationSetParser } from './application-set-parser';
+import { SubscriptionParser } from './subscription-parser';
+
+const getPVCQueryFilter = (
+  name: string,
+  namespace: string,
+  cluster: string
+): PVCQueryFilter => [
+  { property: 'name', values: name },
+  { property: 'kind', values: VirtualMachineModel.kind },
+  { property: 'apigroup', values: VirtualMachineModel.apiGroup },
+  { property: 'namespace', values: namespace },
+  { property: 'cluster', values: cluster },
+];
+
+const getApplicationSetName = (vm: VirtualMachineKind) =>
+  getLabel(
+    vm,
+    KUBE_INSTANCE_LABEL,
+    getLabelsFromSearchResult(vm, true)?.[KUBE_INSTANCE_LABEL]?.[0]
+  );
+
+export const VirtualMachineParser: React.FC<VirtualMachineParserProps> = ({
+  application: virtualMachine,
+  cluster,
+  setCurrentModalContext,
+}) => {
+  const vmName = getName(virtualMachine) || virtualMachine?.name;
+  const vmNamespace = getNamespace(virtualMachine) || virtualMachine?.namespace;
+  const clusterName = cluster || virtualMachine.cluster;
+  const applicationSetName = getApplicationSetName(virtualMachine);
+  const pvcQueryFilter = getPVCQueryFilter(vmName, vmNamespace, clusterName);
+
+  // ACM search API call to find managed application resource
+  const searchQuery = React.useMemo(
+    () =>
+      queryManagedApplicationResourcesForVM(
+        [vmName, applicationSetName],
+        vmNamespace,
+        clusterName
+      ),
+    [vmName, vmNamespace, clusterName, applicationSetName]
+  );
+
+  const [searchResult, searchError, searchLoaded] =
+    useACMSafeFetch(searchQuery);
+
+  // Extract managed application
+  const managedApplication: SearchResultItemType = React.useMemo(() => {
+    if (searchLoaded && !searchError) {
+      return searchResult?.data?.searchResult?.[0]?.related?.[0]?.items?.find(
+        (item) =>
+          !item?.['localPlacement'] || item?.['localPlacement'] === 'false'
+      );
+    }
+  }, [searchResult, searchError, searchLoaded]);
+
+  if (!searchLoaded || searchError) {
+    return (
+      <ModalContextViewer
+        applicationInfo={{}}
+        loadError={searchError}
+        loaded={searchLoaded}
+        matchingPolicies={[]}
+        setCurrentModalContext={setCurrentModalContext}
+      />
+    );
+  }
+
+  if (managedApplication?.kind === ArgoApplicationSetModel.kind) {
+    return (
+      <ApplicationSetParser
+        application={{
+          apiVersion: `${ArgoApplicationSetModel.apiGroup}/${ArgoApplicationSetModel.apiVersion}`,
+          kind: ArgoApplicationSetModel.kind,
+          metadata: {
+            name: managedApplication.name,
+            namespace: managedApplication.namespace,
+          },
+          spec: {},
+        }}
+        setCurrentModalContext={setCurrentModalContext}
+        watchApplication
+        pvcQueryFilter={pvcQueryFilter}
+      />
+    );
+  }
+
+  if (managedApplication?.kind === ACMSubscriptionModel.kind) {
+    return (
+      <SubscriptionParser
+        application={{
+          apiVersion: `${ApplicationModel.apiGroup}/${ApplicationModel.apiVersion}`,
+          kind: ApplicationModel.kind,
+          metadata: {
+            name: getLabelsFromSearchResult(managedApplication)?.app?.[0],
+            namespace: managedApplication.namespace,
+          },
+          spec: {
+            componentKinds: [
+              {
+                group: managedApplication.apigroup,
+                kind: managedApplication.kind,
+              },
+            ],
+          },
+        }}
+        subscriptionName={managedApplication.name}
+        watchApplication
+        setCurrentModalContext={setCurrentModalContext}
+        pvcQueryFilter={pvcQueryFilter}
+      />
+    );
+  }
+  // TODO: Add support for discovered application type
path: packages/mco/components/modals/app-manage-policies/parsers/virtual-machine-parser.tsx
user: login: alfonsomthd
body: This is to be addressed in a follow-up PR, right?
created_at: 2025-02-25T11:11:11Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,13 @@
+import { K8sModel } from '@openshift-console/dynamic-plugin-sdk/lib/api/common-types';
+
+export const VirtualMachineModel: K8sModel = {
+  label: 'VirtualMachine',
+  labelPlural: 'VirtualMachines',
+  apiVersion: 'v1',
+  apiGroup: 'kubevirt.io',
+  plural: 'virtualmachines',
+  abbr: 'VM',
+  namespaced: true,
+  kind: 'VirtualMachine',
+  crd: true,
+};
path: packages/mco/models/kubevirt.ts
user: login: GowthamShanmugam
body: The VirtualMachine (VM) Custom Resource Definition (CRD) belongs to KubeVirt, which enables running virtual machines on Kubernetes.  

The **ApplicationSet (AppSet) and Subscription CRs** serve as **Hub cluster resources** that assist ACM in deploying application workloads onto **managed clusters**. These CRs provide essential deployment metadata, such as:  
- The **GitHub repository** containing the application workloads  
- The **branch** and **path** ACM should monitor for updates  

On the other hand, the **VirtualMachine (VM) CR** represents the **actual application workload** being deployed on the managed cluster, as defined in the GitOps repository.
created_at: 2025-03-03T14:56:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -61,14 +60,17 @@ const getPlacementTags = (pvcSelectors: PVCSelectorType[]): TagsType =>
 
 const getLabelsFromSearchResult = (searchResult: SearchResult): string[] => {
   const pvcLabels =
-    searchResult?.data.searchResult?.[0]?.items.reduce((acc, item) => {
-      const labels: string[] = item[LABEL]?.split(LABELS_SPLIT_CHAR) || [];
-      const filteredLabels = labels?.filter((label) => {
-        const [key] = label.split(LABEL_SPLIT_CHAR);
-        return !DR_BLOCK_LISTED_LABELS.includes(key);
-      });
-      return [...acc, ...filteredLabels];
-    }, []) || [];
+    searchResult?.data.searchResult?.[0]?.related?.[0]?.items?.reduce(
path: packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx
user: login: GowthamShanmugam
body: The **ACM Search API** supports two different query approaches:  

1️⃣ **Query resources** – Directly retrieves specified resources.  
2️⃣ **Query resources along with their related resources** – Fetches a resource and its associated objects.  

### **Previous Approach (Up to ACM 4.18): Querying Only Resources**  
Previously, I used the **first approach**, where the API queried the **application's namespace** to fetch all **Persistent Volume Claims (PVCs)** within it. The labels were then extracted from these PVCs.  

#### **The Issue:**  
- A single **namespace** can contain **multiple applications**.  
- Collecting **labels from all PVCs** in the namespace doesn’t make sense, as they might belong to different applications.  
- This becomes a **major problem for VirtualMachines (VMs)**, since a namespace could have **hundreds of VMs**, leading to inaccurate label collection.  

### **Optimized Approach: Querying Resources with Related Resources**  
To solve this, I switched to the **second approach**, where instead of querying just the namespace, I pass **specific application information** in the query and find **PVCs as related resources**.  

This way, the **search is more precise**, reducing unnecessary label collection and improving accuracy, especially in VM-heavy environments. 

Now the PVC label search is optimized for all the applications including VM.
created_at: 2025-03-03T15:05:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,142 @@
+import * as React from 'react';
+import { KUBE_INSTANCE_LABEL } from '@odf/mco/constants';
+import { useACMSafeFetch } from '@odf/mco/hooks';
+import {
+  ACMSubscriptionModel,
+  ArgoApplicationSetModel,
+  VirtualMachineModel,
+} from '@odf/mco/models';
+import { SearchResultItemType, VirtualMachineKind } from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import { ApplicationModel, getLabel, getName, getNamespace } from '@odf/shared';
+import { ModalContextViewer } from '../modal-context-viewer';
+import { ModalViewContext } from '../utils/reducer';
+import { PVCQueryFilter } from '../utils/types';
+import { ApplicationSetParser } from './application-set-parser';
+import { SubscriptionParser } from './subscription-parser';
+
+const getPVCQueryFilter = (
+  name: string,
+  namespace: string,
+  cluster: string
+): PVCQueryFilter => [
+  { property: 'name', values: name },
+  { property: 'kind', values: VirtualMachineModel.kind },
+  { property: 'apigroup', values: VirtualMachineModel.apiGroup },
+  { property: 'namespace', values: namespace },
+  { property: 'cluster', values: cluster },
+];
+
+const getApplicationSetName = (vm: VirtualMachineKind) =>
+  getLabel(
+    vm,
+    KUBE_INSTANCE_LABEL,
+    getLabelsFromSearchResult(vm, true)?.[KUBE_INSTANCE_LABEL]?.[0]
+  );
+
+export const VirtualMachineParser: React.FC<VirtualMachineParserProps> = ({
+  application: virtualMachine,
+  cluster,
+  setCurrentModalContext,
+}) => {
+  const vmName = getName(virtualMachine) || virtualMachine?.name;
+  const vmNamespace = getNamespace(virtualMachine) || virtualMachine?.namespace;
+  const clusterName = cluster || virtualMachine.cluster;
+  const applicationSetName = getApplicationSetName(virtualMachine);
+  const pvcQueryFilter = getPVCQueryFilter(vmName, vmNamespace, clusterName);
+
+  // ACM search API call to find managed application resource
+  const searchQuery = React.useMemo(
+    () =>
+      queryManagedApplicationResourcesForVM(
+        [vmName, applicationSetName],
+        vmNamespace,
+        clusterName
+      ),
+    [vmName, vmNamespace, clusterName, applicationSetName]
+  );
+
+  const [searchResult, searchError, searchLoaded] =
+    useACMSafeFetch(searchQuery);
+
+  // Extract managed application
+  const managedApplication: SearchResultItemType = React.useMemo(() => {
+    if (searchLoaded && !searchError) {
+      return searchResult?.data?.searchResult?.[0]?.related?.[0]?.items?.find(
+        (item) =>
+          !item?.['localPlacement'] || item?.['localPlacement'] === 'false'
+      );
+    }
+  }, [searchResult, searchError, searchLoaded]);
+
+  if (!searchLoaded || searchError) {
+    return (
+      <ModalContextViewer
+        applicationInfo={{}}
+        loadError={searchError}
+        loaded={searchLoaded}
+        matchingPolicies={[]}
+        setCurrentModalContext={setCurrentModalContext}
+      />
+    );
+  }
+
+  if (managedApplication?.kind === ArgoApplicationSetModel.kind) {
+    return (
+      <ApplicationSetParser
+        application={{
+          apiVersion: `${ArgoApplicationSetModel.apiGroup}/${ArgoApplicationSetModel.apiVersion}`,
+          kind: ArgoApplicationSetModel.kind,
+          metadata: {
+            name: managedApplication.name,
+            namespace: managedApplication.namespace,
+          },
+          spec: {},
+        }}
+        setCurrentModalContext={setCurrentModalContext}
+        watchApplication
+        pvcQueryFilter={pvcQueryFilter}
+      />
+    );
+  }
+
+  if (managedApplication?.kind === ACMSubscriptionModel.kind) {
+    return (
+      <SubscriptionParser
+        application={{
+          apiVersion: `${ApplicationModel.apiGroup}/${ApplicationModel.apiVersion}`,
+          kind: ApplicationModel.kind,
+          metadata: {
+            name: getLabelsFromSearchResult(managedApplication)?.app?.[0],
+            namespace: managedApplication.namespace,
+          },
+          spec: {
+            componentKinds: [
+              {
+                group: managedApplication.apigroup,
+                kind: managedApplication.kind,
+              },
+            ],
+          },
+        }}
+        subscriptionName={managedApplication.name}
+        watchApplication
+        setCurrentModalContext={setCurrentModalContext}
+        pvcQueryFilter={pvcQueryFilter}
+      />
+    );
+  }
+  // TODO: Add support for discovered application type
path: packages/mco/components/modals/app-manage-policies/parsers/virtual-machine-parser.tsx
user: login: GowthamShanmugam
body: Yes, For discovered i going to send a new PR
created_at: 2025-03-03T15:59:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -61,14 +60,17 @@ const getPlacementTags = (pvcSelectors: PVCSelectorType[]): TagsType =>
 
 const getLabelsFromSearchResult = (searchResult: SearchResult): string[] => {
   const pvcLabels =
-    searchResult?.data.searchResult?.[0]?.items.reduce((acc, item) => {
-      const labels: string[] = item[LABEL]?.split(LABELS_SPLIT_CHAR) || [];
-      const filteredLabels = labels?.filter((label) => {
-        const [key] = label.split(LABEL_SPLIT_CHAR);
-        return !DR_BLOCK_LISTED_LABELS.includes(key);
-      });
-      return [...acc, ...filteredLabels];
-    }, []) || [];
+    searchResult?.data.searchResult?.[0]?.related?.[0]?.items?.reduce(
path: packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx
user: login: GowthamShanmugam
body: The **query structure varies for each application type** when retrieving PVCs as a related resource:  

- **ApplicationSet**: No fully optimized method exists yet to fetch PVCs specific to a particular AppSet.  
  [[Code reference](https://github.com/red-hat-storage/odf-console/pull/1856/files#diff-ecc0b5ba3f2a4cf0a22eec5c4aed84f598b5114bfb31fcb8acee4ecee28ddf93R153)](https://github.com/red-hat-storage/odf-console/pull/1856/files#diff-ecc0b5ba3f2a4cf0a22eec5c4aed84f598b5114bfb31fcb8acee4ecee28ddf93R153)  

- **Subscription**: Uses a different query approach to fetch PVCs.  
  [[Code reference](https://github.com/red-hat-storage/odf-console/pull/1856/files#diff-9d38c73396ed2fd491668aaed3c4ef4bb42c6a5abcfd44246f64db867e765fa6R152)](https://github.com/red-hat-storage/odf-console/pull/1856/files#diff-9d38c73396ed2fd491668aaed3c4ef4bb42c6a5abcfd44246f64db867e765fa6R152)  

- **VirtualMachine**: Implements a specific query pattern to retrieve PVCs linked to VMs.  
  [[Code reference](https://github.com/red-hat-storage/odf-console/pull/1856/files#diff-ff0ca4a4bc6bf6663e8dfc64c966dbc82f9e93ed7cabe1a61e49d12d85d30571R21)](https://github.com/red-hat-storage/odf-console/pull/1856/files#diff-ff0ca4a4bc6bf6663e8dfc64c966dbc82f9e93ed7cabe1a61e49d12d85d30571R21)  

The **query logic adapts based on the application type** to ensure PVCs are fetched as a **related resource** efficiently.  
[[General reference](https://github.com/red-hat-storage/odf-console/pull/1856/files#diff-fa1c38820272c29074685a363ab3e360bcbe7ac914fc8a68a3864fb432f1ebdfR39)](https://github.com/red-hat-storage/odf-console/pull/1856/files#diff-fa1c38820272c29074685a363ab3e360bcbe7ac914fc8a68a3864fb432f1ebdfR39)
created_at: 2025-03-03T16:05:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,144 +1,70 @@
 import * as React from 'react';
-import { DRApplication } from '@odf/mco/constants';
+import { SearchResultItemType } from '@odf/mco/types';
+import { convertSearchResult } from '@odf/mco/utils';
 import { getName, getNamespace } from '@odf/shared/selectors';
 import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
 import { Trans } from 'react-i18next';
 import { Modal, ModalVariant } from '@patternfly/react-core';
-import { AssignPolicyView } from './assign-policy-view';
-import { ManagePolicyView } from './manage-policy-view';
-import {
-  ManagePolicyStateAction,
-  ManagePolicyStateType,
-  ModalActionContext,
-  ModalViewContext,
-  initialPolicyState,
-  managePolicyStateReducer,
-  ManagePolicyState,
-} from './utils/reducer';
-import { ApplicationType, DRInfoType, DRPolicyType } from './utils/types';
+import { AppManagePoliciesModalBody } from './app-manage-policies-modal-body';
+import { ModalViewContext } from './utils/reducer';
 
-export const ModalContextViewer: React.FC<ModalContextViewerProps> = ({
-  applicationInfo,
-  state,
-  matchingPolicies,
-  dispatch,
-  loaded,
-  loadError,
+export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
+  resource,
+  cluster,
+  isOpen,
+  close,
 }) => {
-  const setModalContext = React.useCallback(
-    (modalViewContext: ModalViewContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
-        payload: modalViewContext,
-      }),
-    [dispatch]
+  const { t } = useCustomTranslation();
+  const [currentModalContext, setCurrentModalContext] = React.useState(
+    ModalViewContext.MANAGE_POLICY_VIEW
   );
 
-  const setModalActionContext = React.useCallback(
-    (modalActionContext: ModalActionContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
-        payload: modalActionContext,
-      }),
-    [dispatch]
-  );
+  const application =
+    'apigroup' in resource ? convertSearchResult(resource) : resource;
+  const applicationName = getName(application) ?? application?.['name'];
+  const applicationNamespace =
+    getNamespace(application) ?? application?.['namespace'];
 
-  return (
-    <>
-      {state.modalViewContext === ModalViewContext.MANAGE_POLICY_VIEW && (
-        <ManagePolicyView
-          drInfo={applicationInfo?.drInfo as DRInfoType}
-          workloadNamespace={applicationInfo?.workloadNamespace}
-          eligiblePolicies={matchingPolicies}
-          isSubscriptionAppType={
-            applicationInfo?.type === DRApplication.SUBSCRIPTION
-          }
-          unProtectedPlacementCount={applicationInfo?.placements?.length}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-          loaded={loaded}
-          loadError={loadError}
-          modalActionContext={state.modalActionContext}
-        />
-      )}
-      {state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW && (
-        <AssignPolicyView
-          applicationInfo={applicationInfo}
-          matchingPolicies={matchingPolicies}
-          state={state.assignPolicyView}
-          modalActionContext={state.modalActionContext}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-        />
-      )}
-    </>
-  );
-};
+  const title =
+    currentModalContext === ModalViewContext.ASSIGN_POLICY_VIEW
+      ? t('Enroll managed application')
+      : t('Manage disaster recovery');
 
-export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
-  applicationInfo,
-  matchingPolicies,
-  loaded,
-  loadError,
-  isOpen,
-  close,
-}) => {
-  const [state, dispatch] = React.useReducer(
-    managePolicyStateReducer,
-    initialPolicyState
+  const description = (
+    <Trans t={t}>
+      <strong>Application:</strong> {applicationName} (Namespace:{' '}
+      {applicationNamespace})
+    </Trans>
   );
-  const { t } = useCustomTranslation();
 
   return (
     <Modal
-      title={
-        state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW
-          ? t('Enroll managed application')
-          : t('Manage disaster recovery')
-      }
-      description={
-        loaded &&
-        !loadError && (
-          <Trans t={t}>
-            <strong>Application:</strong> {getName(applicationInfo)} (Namespace:{' '}
-            {getNamespace(applicationInfo)})
-          </Trans>
-        )
-      }
+      title={title}
+      description={description}
       variant={ModalVariant.large}
       isOpen={isOpen}
       aria-label="Manage policy modal"
       aria-describedby="manage-policy-modal"
       onClose={close}
     >
-      <ModalContextViewer
-        applicationInfo={applicationInfo}
-        matchingPolicies={matchingPolicies}
-        state={state}
-        dispatch={dispatch}
-        loaded={loaded}
-        loadError={loadError}
+      <AppManagePoliciesModalBody
+        application={application}
+        cluster={cluster}
+        setCurrentModalContext={setCurrentModalContext}
       />
     </Modal>
   );
 };
 
+// ACM action custom plugin callback,
+// For more: https://github.com/stolostron/console/blob/main/frontend/src/plugin-extensions/properties/addActionProps.ts#L27
path: packages/mco/components/modals/app-manage-policies/app-manage-policies-modal.tsx
user: login: SanjalKatiyar
body: this URL is not working...
created_at: 2025-03-10T14:22:34Z
author_association: COLLABORATOR
diff_hunk: @@ -61,14 +60,17 @@ const getPlacementTags = (pvcSelectors: PVCSelectorType[]): TagsType =>
 
 const getLabelsFromSearchResult = (searchResult: SearchResult): string[] => {
   const pvcLabels =
-    searchResult?.data.searchResult?.[0]?.items.reduce((acc, item) => {
-      const labels: string[] = item[LABEL]?.split(LABELS_SPLIT_CHAR) || [];
-      const filteredLabels = labels?.filter((label) => {
-        const [key] = label.split(LABEL_SPLIT_CHAR);
-        return !DR_BLOCK_LISTED_LABELS.includes(key);
-      });
-      return [...acc, ...filteredLabels];
-    }, []) || [];
+    searchResult?.data.searchResult?.[0]?.related?.[0]?.items?.reduce(
path: packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx
user: login: alfonsomthd
body: To confirm as per the original answer, is this backward compatible?
created_at: 2025-03-10T14:38:57Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,51 @@
+import * as React from 'react';
+import { ArgoApplicationSetModel, VirtualMachineModel } from '@odf/mco/models';
+import { ApplicationModel } from '@odf/shared/models';
+import {
+  getGVKofResource,
+  referenceFor,
+  referenceForModel,
+} from '@odf/shared/utils';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  VirtualMachineParser,
+} from './parsers';
+import { ModalViewContext } from './utils/reducer';
+
+const getGVK = (resource: K8sResourceCommon) =>
+  resource?.['apigroup']
path: packages/mco/components/modals/app-manage-policies/app-manage-policies-modal-body.tsx
user: login: alfonsomthd
body: @GowthamShanmugam is this addressed/answered?
created_at: 2025-03-10T14:41:29Z
author_association: COLLABORATOR
diff_hunk: @@ -1,144 +1,70 @@
 import * as React from 'react';
-import { DRApplication } from '@odf/mco/constants';
+import { SearchResultItemType } from '@odf/mco/types';
+import { convertSearchResult } from '@odf/mco/utils';
 import { getName, getNamespace } from '@odf/shared/selectors';
 import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
 import { Trans } from 'react-i18next';
 import { Modal, ModalVariant } from '@patternfly/react-core';
-import { AssignPolicyView } from './assign-policy-view';
-import { ManagePolicyView } from './manage-policy-view';
-import {
-  ManagePolicyStateAction,
-  ManagePolicyStateType,
-  ModalActionContext,
-  ModalViewContext,
-  initialPolicyState,
-  managePolicyStateReducer,
-  ManagePolicyState,
-} from './utils/reducer';
-import { ApplicationType, DRInfoType, DRPolicyType } from './utils/types';
+import { AppManagePoliciesModalBody } from './app-manage-policies-modal-body';
+import { ModalViewContext } from './utils/reducer';
 
-export const ModalContextViewer: React.FC<ModalContextViewerProps> = ({
-  applicationInfo,
-  state,
-  matchingPolicies,
-  dispatch,
-  loaded,
-  loadError,
+export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
+  resource,
+  cluster,
+  isOpen,
+  close,
 }) => {
-  const setModalContext = React.useCallback(
-    (modalViewContext: ModalViewContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
-        payload: modalViewContext,
-      }),
-    [dispatch]
+  const { t } = useCustomTranslation();
+  const [currentModalContext, setCurrentModalContext] = React.useState(
+    ModalViewContext.MANAGE_POLICY_VIEW
   );
 
-  const setModalActionContext = React.useCallback(
-    (modalActionContext: ModalActionContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
-        payload: modalActionContext,
-      }),
-    [dispatch]
-  );
+  const application =
+    'apigroup' in resource ? convertSearchResult(resource) : resource;
+  const applicationName = getName(application) ?? application?.['name'];
+  const applicationNamespace =
+    getNamespace(application) ?? application?.['namespace'];
 
-  return (
-    <>
-      {state.modalViewContext === ModalViewContext.MANAGE_POLICY_VIEW && (
-        <ManagePolicyView
-          drInfo={applicationInfo?.drInfo as DRInfoType}
-          workloadNamespace={applicationInfo?.workloadNamespace}
-          eligiblePolicies={matchingPolicies}
-          isSubscriptionAppType={
-            applicationInfo?.type === DRApplication.SUBSCRIPTION
-          }
-          unProtectedPlacementCount={applicationInfo?.placements?.length}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-          loaded={loaded}
-          loadError={loadError}
-          modalActionContext={state.modalActionContext}
-        />
-      )}
-      {state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW && (
-        <AssignPolicyView
-          applicationInfo={applicationInfo}
-          matchingPolicies={matchingPolicies}
-          state={state.assignPolicyView}
-          modalActionContext={state.modalActionContext}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-        />
-      )}
-    </>
-  );
-};
+  const title =
+    currentModalContext === ModalViewContext.ASSIGN_POLICY_VIEW
+      ? t('Enroll managed application')
+      : t('Manage disaster recovery');
 
-export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
-  applicationInfo,
-  matchingPolicies,
-  loaded,
-  loadError,
-  isOpen,
-  close,
-}) => {
-  const [state, dispatch] = React.useReducer(
-    managePolicyStateReducer,
-    initialPolicyState
+  const description = (
+    <Trans t={t}>
+      <strong>Application:</strong> {applicationName} (Namespace:{' '}
+      {applicationNamespace})
+    </Trans>
   );
-  const { t } = useCustomTranslation();
 
   return (
     <Modal
-      title={
-        state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW
-          ? t('Enroll managed application')
-          : t('Manage disaster recovery')
-      }
-      description={
-        loaded &&
-        !loadError && (
-          <Trans t={t}>
-            <strong>Application:</strong> {getName(applicationInfo)} (Namespace:{' '}
-            {getNamespace(applicationInfo)})
-          </Trans>
-        )
-      }
+      title={title}
+      description={description}
       variant={ModalVariant.large}
       isOpen={isOpen}
       aria-label="Manage policy modal"
       aria-describedby="manage-policy-modal"
       onClose={close}
     >
-      <ModalContextViewer
-        applicationInfo={applicationInfo}
-        matchingPolicies={matchingPolicies}
-        state={state}
-        dispatch={dispatch}
-        loaded={loaded}
-        loadError={loadError}
+      <AppManagePoliciesModalBody
+        application={application}
+        cluster={cluster}
+        setCurrentModalContext={setCurrentModalContext}
       />
     </Modal>
   );
 };
 
+// ACM action custom plugin callback,
+// For more: https://github.com/stolostron/console/blob/main/frontend/src/plugin-extensions/properties/addActionProps.ts#L27
path: packages/mco/components/modals/app-manage-policies/app-manage-policies-modal.tsx
user: login: alfonsomthd
body: @GowthamShanmugam any update on this?
created_at: 2025-03-11T13:28:48Z
author_association: COLLABORATOR
diff_hunk: @@ -61,14 +60,17 @@ const getPlacementTags = (pvcSelectors: PVCSelectorType[]): TagsType =>
 
 const getLabelsFromSearchResult = (searchResult: SearchResult): string[] => {
   const pvcLabels =
-    searchResult?.data.searchResult?.[0]?.items.reduce((acc, item) => {
-      const labels: string[] = item[LABEL]?.split(LABELS_SPLIT_CHAR) || [];
-      const filteredLabels = labels?.filter((label) => {
-        const [key] = label.split(LABEL_SPLIT_CHAR);
-        return !DR_BLOCK_LISTED_LABELS.includes(key);
-      });
-      return [...acc, ...filteredLabels];
-    }, []) || [];
+    searchResult?.data.searchResult?.[0]?.related?.[0]?.items?.reduce(
path: packages/mco/components/modals/app-manage-policies/helper/pvc-details-wizard-content.tsx
user: login: GowthamShanmugam
body: yes, it is backward compatible.
created_at: 2025-03-11T13:31:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,51 @@
+import * as React from 'react';
+import { ArgoApplicationSetModel, VirtualMachineModel } from '@odf/mco/models';
+import { ApplicationModel } from '@odf/shared/models';
+import {
+  getGVKofResource,
+  referenceFor,
+  referenceForModel,
+} from '@odf/shared/utils';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  VirtualMachineParser,
+} from './parsers';
+import { ModalViewContext } from './utils/reducer';
+
+const getGVK = (resource: K8sResourceCommon) =>
+  resource?.['apigroup']
path: packages/mco/components/modals/app-manage-policies/app-manage-policies-modal-body.tsx
user: login: GowthamShanmugam
body: yes. it is resolved 
created_at: 2025-03-11T13:31:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,144 +1,70 @@
 import * as React from 'react';
-import { DRApplication } from '@odf/mco/constants';
+import { SearchResultItemType } from '@odf/mco/types';
+import { convertSearchResult } from '@odf/mco/utils';
 import { getName, getNamespace } from '@odf/shared/selectors';
 import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
+import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
 import { Trans } from 'react-i18next';
 import { Modal, ModalVariant } from '@patternfly/react-core';
-import { AssignPolicyView } from './assign-policy-view';
-import { ManagePolicyView } from './manage-policy-view';
-import {
-  ManagePolicyStateAction,
-  ManagePolicyStateType,
-  ModalActionContext,
-  ModalViewContext,
-  initialPolicyState,
-  managePolicyStateReducer,
-  ManagePolicyState,
-} from './utils/reducer';
-import { ApplicationType, DRInfoType, DRPolicyType } from './utils/types';
+import { AppManagePoliciesModalBody } from './app-manage-policies-modal-body';
+import { ModalViewContext } from './utils/reducer';
 
-export const ModalContextViewer: React.FC<ModalContextViewerProps> = ({
-  applicationInfo,
-  state,
-  matchingPolicies,
-  dispatch,
-  loaded,
-  loadError,
+export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
+  resource,
+  cluster,
+  isOpen,
+  close,
 }) => {
-  const setModalContext = React.useCallback(
-    (modalViewContext: ModalViewContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_VIEW_CONTEXT,
-        payload: modalViewContext,
-      }),
-    [dispatch]
+  const { t } = useCustomTranslation();
+  const [currentModalContext, setCurrentModalContext] = React.useState(
+    ModalViewContext.MANAGE_POLICY_VIEW
   );
 
-  const setModalActionContext = React.useCallback(
-    (modalActionContext: ModalActionContext) =>
-      dispatch({
-        type: ManagePolicyStateType.SET_MODAL_ACTION_CONTEXT,
-        payload: modalActionContext,
-      }),
-    [dispatch]
-  );
+  const application =
+    'apigroup' in resource ? convertSearchResult(resource) : resource;
+  const applicationName = getName(application) ?? application?.['name'];
+  const applicationNamespace =
+    getNamespace(application) ?? application?.['namespace'];
 
-  return (
-    <>
-      {state.modalViewContext === ModalViewContext.MANAGE_POLICY_VIEW && (
-        <ManagePolicyView
-          drInfo={applicationInfo?.drInfo as DRInfoType}
-          workloadNamespace={applicationInfo?.workloadNamespace}
-          eligiblePolicies={matchingPolicies}
-          isSubscriptionAppType={
-            applicationInfo?.type === DRApplication.SUBSCRIPTION
-          }
-          unProtectedPlacementCount={applicationInfo?.placements?.length}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-          loaded={loaded}
-          loadError={loadError}
-          modalActionContext={state.modalActionContext}
-        />
-      )}
-      {state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW && (
-        <AssignPolicyView
-          applicationInfo={applicationInfo}
-          matchingPolicies={matchingPolicies}
-          state={state.assignPolicyView}
-          modalActionContext={state.modalActionContext}
-          dispatch={dispatch}
-          setModalContext={setModalContext}
-          setModalActionContext={setModalActionContext}
-        />
-      )}
-    </>
-  );
-};
+  const title =
+    currentModalContext === ModalViewContext.ASSIGN_POLICY_VIEW
+      ? t('Enroll managed application')
+      : t('Manage disaster recovery');
 
-export const AppManagePoliciesModal: React.FC<AppManagePoliciesModalProps> = ({
-  applicationInfo,
-  matchingPolicies,
-  loaded,
-  loadError,
-  isOpen,
-  close,
-}) => {
-  const [state, dispatch] = React.useReducer(
-    managePolicyStateReducer,
-    initialPolicyState
+  const description = (
+    <Trans t={t}>
+      <strong>Application:</strong> {applicationName} (Namespace:{' '}
+      {applicationNamespace})
+    </Trans>
   );
-  const { t } = useCustomTranslation();
 
   return (
     <Modal
-      title={
-        state.modalViewContext === ModalViewContext.ASSIGN_POLICY_VIEW
-          ? t('Enroll managed application')
-          : t('Manage disaster recovery')
-      }
-      description={
-        loaded &&
-        !loadError && (
-          <Trans t={t}>
-            <strong>Application:</strong> {getName(applicationInfo)} (Namespace:{' '}
-            {getNamespace(applicationInfo)})
-          </Trans>
-        )
-      }
+      title={title}
+      description={description}
       variant={ModalVariant.large}
       isOpen={isOpen}
       aria-label="Manage policy modal"
       aria-describedby="manage-policy-modal"
       onClose={close}
     >
-      <ModalContextViewer
-        applicationInfo={applicationInfo}
-        matchingPolicies={matchingPolicies}
-        state={state}
-        dispatch={dispatch}
-        loaded={loaded}
-        loadError={loadError}
+      <AppManagePoliciesModalBody
+        application={application}
+        cluster={cluster}
+        setCurrentModalContext={setCurrentModalContext}
       />
     </Modal>
   );
 };
 
+// ACM action custom plugin callback,
+// For more: https://github.com/stolostron/console/blob/main/frontend/src/plugin-extensions/properties/addActionProps.ts#L27
path: packages/mco/components/modals/app-manage-policies/app-manage-policies-modal.tsx
user: login: GowthamShanmugam
body: I recently renamed the file in ACM console, I have to update this URL
created_at: 2025-03-11T13:32:15Z
author_association: CONTRIBUTOR

PR comments:
user: login: GowthamShanmugam
created_at: 2025-02-17T11:36:07Z
body: <img width="1716" alt="Screenshot 2025-02-17 at 5 03 25 PM" src="https://github.com/user-attachments/assets/ceb4993b-5728-4de2-9410-fa6c344fd163" />
<img width="1716" alt="Screenshot 2025-02-17 at 5 04 00 PM" src="https://github.com/user-attachments/assets/01291e97-b412-4ba7-997b-1a8d583cde05" />
<img width="1716" alt="Screenshot 2025-02-17 at 5 05 07 PM" src="https://github.com/user-attachments/assets/0f7a4e07-120f-4259-8903-21c5aaf8c2dd" />
<img width="1716" alt="Screenshot 2025-02-17 at 5 03 14 PM" src="https://github.com/user-attachments/assets/2f252572-ad64-486f-af5f-25dd5b9001d6" />
<img width="1716" alt="Screenshot 2025-02-17 at 5 09 27 PM" src="https://github.com/user-attachments/assets/5b5f6de3-680b-4a61-81e9-59dddf3fdc82" />


author_association: CONTRIBUTOR
user: login: openshift-ci-robot
created_at: 2025-02-17T12:57:47Z
body: @GowthamShanmugam: This pull request references [RHSTOR-6968](https://issues.redhat.com//browse/RHSTOR-6968) which is a valid jira issue.

<details>

In response to [this](https://github.com/red-hat-storage/odf-console/pull/1856):

>ACM custom extension: https://github.com/stolostron/console/pull/4252


Instructions for interacting with me using PR comments are available [here](https://prow.ci.openshift.org/command-help?repo=red-hat-storage%2Fodf-console).  If you have questions or suggestions related to my behavior, please file an issue against the [openshift-eng/jira-lifecycle-plugin](https://github.com/openshift-eng/jira-lifecycle-plugin/issues/new) repository.
</details>
author_association: NONE
user: login: openshift-ci-robot
created_at: 2025-02-24T15:38:56Z
body: @GowthamShanmugam: This pull request references [RHSTOR-6968](https://issues.redhat.com//browse/RHSTOR-6968) which is a valid jira issue.

<details>

In response to [this](https://github.com/red-hat-storage/odf-console/pull/1856):

>ACM custom extension: https://github.com/stolostron/console/pull/4252
>
>**Existing Flow:**
>The parser always triggers the modal, even while the watcher is fetching data, displaying it with a loading: false argument.
>
><img width="934" alt="Screenshot 2025-02-17 at 6 10 29 PM" src="https://github.com/user-attachments/assets/df0b0add-a1a4-4219-8198-7dd428116176" />
>
>
>**Problem in existing flow for VM:** (Delay in modal loading)
>The application parser is determined only after the VM parser identifies the appropriate parser to call. This causes a delay in modal loading, making the modal appear a few seconds after the user clicks the button.
>
><img width="947" alt="Screenshot 2025-02-17 at 6 10 43 PM" src="https://github.com/user-attachments/assets/a215c921-44e8-461a-9351-00b779a78f3b" />
>
>
>**New Flow:**
>The modal will always load, while the modal body will be rendered based on the parsers.
>
><img width="976" alt="Screenshot 2025-02-17 at 6 24 27 PM" src="https://github.com/user-attachments/assets/621916db-016d-46dd-921e-38350bc55aaa" />
>
>
>
>
>
>
>


Instructions for interacting with me using PR comments are available [here](https://prow.ci.openshift.org/command-help?repo=red-hat-storage%2Fodf-console).  If you have questions or suggestions related to my behavior, please file an issue against the [openshift-eng/jira-lifecycle-plugin](https://github.com/openshift-eng/jira-lifecycle-plugin/issues/new) repository.
</details>
author_association: NONE
user: login: alfonsomthd
created_at: 2025-03-11T13:40:40Z
body: LGTM. @SanjalKatiyar @bipuladh to tag.
author_association: COLLABORATOR
user: login: SanjalKatiyar
created_at: 2025-03-12T13:28:20Z
body: /lgtm
author_association: COLLABORATOR
user: login: openshift-ci[bot]
created_at: 2025-03-12T13:28:27Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/red-hat-storage/odf-console/pull/1856#" title="Author self-approved">GowthamShanmugam</a>*, *<a href="https://github.com/red-hat-storage/odf-console/pull/1856#issuecomment-2717889039" title="LGTM">SanjalKatiyar</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=red-hat-storage%2Fodf-console).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/red-hat-storage/odf-console/blob/master/OWNERS)~~ [GowthamShanmugam,SanjalKatiyar]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR
user: login: GowthamShanmugam
created_at: 2025-03-13T08:37:22Z
body: /test image
author_association: CONTRIBUTOR
user: login: openshift-ci[bot]
created_at: 2025-03-13T08:37:27Z
body: @GowthamShanmugam: The specified target(s) for `/test` were not found.
The following commands are available to trigger required jobs:
```
/test images
```
```
/test odf-console-e2e-aws
```

Use `/test all` to run all jobs.


<details>

In response to [this](https://github.com/red-hat-storage/odf-console/pull/1856#issuecomment-2720388810):

>/test image


Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>
author_association: CONTRIBUTOR
user: login: GowthamShanmugam
created_at: 2025-03-13T08:37:55Z
body: /override ci/prow/odf-console-e2e-aws MCO doesn't have any e2e testing.
author_association: CONTRIBUTOR
user: login: openshift-ci[bot]
created_at: 2025-03-13T08:38:01Z
body: @GowthamShanmugam: /override requires failed status contexts, check run or a prowjob name to operate on.
The following unknown contexts/checkruns were given:
 - `MCO`
 - `any`
 - `doesn't`
 - `e2e`
 - `have`
 - `testing.`

Only the following failed contexts/checkruns were expected:
 - `ci/prow/images`
 - `ci/prow/odf-console-e2e-aws`
 - `tide`

If you are trying to override a checkrun that has a space in it, you must put a double quote on the context.


<details>

In response to [this](https://github.com/red-hat-storage/odf-console/pull/1856#issuecomment-2720390315):

>/override ci/prow/odf-console-e2e-aws MCO doesn't have any e2e testing.


Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>
author_association: CONTRIBUTOR
user: login: GowthamShanmugam
created_at: 2025-03-13T08:38:29Z
body: /override ci/prow/odf-console-e2e-aws
author_association: CONTRIBUTOR
user: login: openshift-ci[bot]
created_at: 2025-03-13T08:38:39Z
body: @GowthamShanmugam: Overrode contexts on behalf of GowthamShanmugam: ci/prow/odf-console-e2e-aws

<details>

In response to [this](https://github.com/red-hat-storage/odf-console/pull/1856#issuecomment-2720392747):

>/override ci/prow/odf-console-e2e-aws


Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>
author_association: CONTRIBUTOR
user: login: GowthamShanmugam
created_at: 2025-03-13T08:39:18Z
body: /test images
author_association: CONTRIBUTOR