Repository: red-hat-storage/odf-console
Pull request #1916: RHSTOR-7186: DR Status popover for virtual machine list page.
Author: login: TimothyAsirJeyasing
Labels: name: approved
name: lgtm
name: jira/valid-reference
description: Indicates that this PR references a valid jira ticket of any type
Created: 2025-03-21T09:21:06Z | Merged: 2025-03-25T08:19:04Z

https://issues.redhat.com/browse/RHSTOR-7186


Diff:
diff --git a/locales/en/plugin__odf-console.json b/locales/en/plugin__odf-console.json
index d9b7294be..df9e00757 100644
--- a/locales/en/plugin__odf-console.json
+++ b/locales/en/plugin__odf-console.json
@@ -158,13 +158,17 @@
   "DR Policy:": "DR Policy:",
   "{{policyName}}, sync every {{schedulingInterval}}": "{{policyName}}, sync every {{schedulingInterval}}",
   "{{ label }} {{ value }}": "{{ label }} {{ value }}",
-  "Open documentation": "Open documentation",
+  "Documentation help link": "Documentation help link",
+  "Learn about different failover status": "Learn about different failover status",
+  "Learn about different relocate status": "Learn about different relocate status",
+  "How to clean up resources?": "How to clean up resources?",
+  "Clean up application resources on failed cluster {{cluster}}.": "Clean up application resources on failed cluster {{cluster}}.",
+  "Clean up application resources on the primary cluster {{cluster}} to start the relocation.": "Clean up application resources on the primary cluster {{cluster}} to start the relocation.",
   "All volumes & Kubernetes resources are synced": "All volumes & Kubernetes resources are synced",
   "All volumes are synced": "All volumes are synced",
   "Status unknown": "Status unknown",
   "The current status could not be determined.": "The current status could not be determined.",
   "Action needed": "Action needed",
-  "Clean up application resources on failed cluster {{cluster}} to start the replication.": "Clean up application resources on failed cluster {{cluster}} to start the replication.",
   "Failover in progress": "Failover in progress",
   "Deploying the application on the target cluster.": "Deploying the application on the target cluster.",
   "Relocate in progress": "Relocate in progress",
diff --git a/packages/mco/components/dr-status-popover/acm-column-callback.tsx b/packages/mco/components/dr-status-popover/acm-column-callback.tsx
index d2805a827..9d24b198a 100644
--- a/packages/mco/components/dr-status-popover/acm-column-callback.tsx
+++ b/packages/mco/components/dr-status-popover/acm-column-callback.tsx
@@ -1,25 +1,31 @@
 import * as React from 'react';
 import { ArgoApplicationSetKind } from '@odf/mco/types';
-import { getGVKFromK8Resource } from '@odf/mco/utils';
-import { ArgoApplicationSetModel } from '@odf/shared';
+import { convertSearchResult, getGVKFromK8Resource } from '@odf/mco/utils';
+import { ArgoApplicationSetModel, VirtualMachineModel } from '@odf/shared';
 import { ApplicationModel } from '@odf/shared/models';
 import { ApplicationKind } from '@odf/shared/types';
 import { referenceForModel } from '@odf/shared/utils';
 import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
 import { ApplicationSetParser, SubscriptionParser } from './parsers';
+import VirtualMachineParser from './parsers/virtualmachine-parser';
 
 const DRStatus: React.FC<DRStatusProps> = ({ resource }) => {
-  const gvk = getGVKFromK8Resource(resource);
+  const application =
+    'apigroup' in resource ? convertSearchResult(resource as any) : resource;
+  const gvk = getGVKFromK8Resource(application);
 
   return (
     <>
       {gvk === referenceForModel(ArgoApplicationSetModel) && (
         <ApplicationSetParser
-          application={resource as ArgoApplicationSetKind}
+          application={application as ArgoApplicationSetKind}
         />
       )}
       {gvk === referenceForModel(ApplicationModel) && (
-        <SubscriptionParser application={resource as ApplicationKind} />
+        <SubscriptionParser application={application as ApplicationKind} />
+      )}
+      {gvk === referenceForModel(VirtualMachineModel) && (
+        <VirtualMachineParser virtualMachine={application as any} />
       )}
     </>
   );
diff --git a/packages/mco/components/dr-status-popover/dr-status-popover.tsx b/packages/mco/components/dr-status-popover/dr-status-popover.tsx
index d6bc0903d..6adf87014 100644
--- a/packages/mco/components/dr-status-popover/dr-status-popover.tsx
+++ b/packages/mco/components/dr-status-popover/dr-status-popover.tsx
@@ -1,13 +1,19 @@
 import * as React from 'react';
-import { DRPCStatus, VolumeReplicationHealth } from '@odf/mco/constants';
+import {
+  DRPCStatus,
+  drStatusPopoverDocs,
+  VolumeReplicationHealth,
+} from '@odf/mco/constants';
 import { Progression } from '@odf/mco/types';
 import { formatTime } from '@odf/shared/details-page/datetime';
+import { DOC_VERSION as mcoDocVersion } from '@odf/shared/hooks';
 import {
   GreenCheckCircleIcon,
   RedExclamationCircleIcon,
   YellowExclamationTriangleIcon,
 } from '@odf/shared/status/icons';
 import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
+import { ViewDocumentation } from '@odf/shared/utils';
 import { StatusIconAndText } from '@openshift-console/dynamic-plugin-sdk';
 import { TFunction } from 'react-i18next';
 import {
@@ -158,44 +164,28 @@ const SyncStatus: React.FC<{
   );
 };
 
-const HelpLink: React.FC<{
-  href: string;
-  t: TFunction;
-  message: string;
-}> = ({ href, t, message }) => {
-  return (
-    <Button
-      className="dr-status-help-link"
-      variant={ButtonVariant.link}
-      isInline
-      href={href}
-      target="_blank"
-      rel="noopener noreferrer"
-      aria-label={t('Open documentation')}
-    >
-      {message}
-    </Button>
-  );
-};
-
-const statusHelpLinks = {
+const statusHelpLinks = (t: TFunction) => ({
   [DRStatus.Warning]: {
-    href: '', // TODO: Replace with the actual link
-    message: 'Documentation help link',
+    href: drStatusPopoverDocs(mcoDocVersion).VOLUME_SYNC_DELAY,
+    message: t('Documentation help link'),
   },
   [DRStatus.Critical]: {
-    href: '', // TODO: Replace with the actual link
-    message: 'Documentation help link',
+    href: drStatusPopoverDocs(mcoDocVersion).VOLUME_SYNC_DELAY,
+    message: t('Documentation help link'),
   },
   [DRStatus.FailingOver]: {
-    href: '', // TODO: Replace with the actual link
-    message: 'Learn about different failover status',
+    href: drStatusPopoverDocs(mcoDocVersion).FAILOVER,
+    message: t('Learn about different failover status'),
   },
   [DRStatus.Relocating]: {
-    href: '', // TODO: Replace with the actual link
-    message: 'Learn about different relocate status',
+    href: drStatusPopoverDocs(mcoDocVersion).RELOCATION,
+    message: t('Learn about different relocate status'),
   },
-};
+  [DRStatus.WaitOnUserToCleanUp]: {
+    href: drStatusPopoverDocs(mcoDocVersion).CLEANUP,
+    message: t('How to clean up resources?'),
+  },
+});
 
 const handleHealthStatus = ({
   status,
@@ -298,6 +288,21 @@ const getDRStatus = ({
   return DRStatus.Critical;
 };
 
+const getCleanupMessage = (
+  phase: DRPCStatus,
+  cluster: string,
+  t: TFunction
+) => {
+  return phase === DRPCStatus.FailingOver
+    ? t('Clean up application resources on failed cluster {{cluster}}.', {
+        cluster,
+      })
+    : t(
+        'Clean up application resources on the primary cluster {{cluster}} to start the relocation.',
+        { cluster }
+      );
+};
+
 const getDRStatusDetails = ({
   isCleanupRequired,
   phase,
@@ -377,12 +382,10 @@ const getDRStatusDetails = ({
       return createStatus(
         DRStatus.WaitOnUserToCleanUp,
         t('Action needed'),
-        t(
-          'Clean up application resources on failed cluster {{cluster}} to start the replication.',
-          {
-            cluster:
-              phase === DRPCStatus.FailingOver ? targetCluster : primaryCluster,
-          }
+        getCleanupMessage(
+          phase,
+          phase === DRPCStatus.FailingOver ? targetCluster : primaryCluster,
+          t
         ),
         'dr-status-action-needed'
       );
@@ -438,7 +441,7 @@ const DRStatusPopover: React.FC<DRStatusPopoverProps> = ({
     setIsVisible((prev) => !prev);
   };
 
-  const statusLink = statusHelpLinks[status];
+  const statusLink = statusHelpLinks(t)[status];
 
   return (
     disasterRecoveryStatus.isLoadedWOError && (
@@ -515,10 +518,10 @@ const DRStatusPopover: React.FC<DRStatusPopoverProps> = ({
             )}
 
             {statusLink && (
-              <HelpLink
-                href={statusLink.href}
-                t={t}
-                message={t(statusLink.message)}
+              <ViewDocumentation
+                doclink={statusLink.href}
+                text={statusLink.message}
+                padding="0"
               />
             )}
           </Flex>
diff --git a/packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx b/packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
new file mode 100644
index 000000000..682e2cabd
--- /dev/null
+++ b/packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
@@ -0,0 +1,149 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  KUBE_INSTANCE_LABEL,
+  ODF_RESOURCE_TYPE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import {
+  ACMSubscriptionModel,
+  ApplicationKind,
+  ArgoApplicationSetModel,
+  getLabel,
+  getName,
+  getNamespace,
+  VirtualMachineModel,
+} from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import { findDRPCUsingVM } from '../../modals/app-manage-policies/utils/parser-utils';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<ParserHelperProps> = ({
+  vmName,
+  vmNamespace,
+}) => {
+  const [resource, loaded, loadError] =
+    useK8sWatchResource<ArgoApplicationSetKind>(
+      getApplicationSetResourceObj({ name: vmName, namespace: vmNamespace })
+    );
+
+  if (!loaded || loadError) return null;
+  return <ApplicationSetParser application={resource} />;
+};
+
+const SubscriptionHelper: React.FC<ParserHelperProps> = ({
+  vmName,
+  vmNamespace,
+}) => {
+  const [resource, loaded, loadError] = useK8sWatchResource<ApplicationKind>(
+    getApplicationResourceObj({ name: vmName, namespace: vmNamespace })
+  );
+
+  if (!loaded || loadError) return null;
+  return <SubscriptionParser application={resource} />;
+};
+
+const DiscoveredHelper: React.FC<ParserHelperProps> = ({
+  vmName,
+  vmNamespace,
+}) => {
+  const [drpcs, loaded, loadError] = useK8sWatchResource<
+    DRPlacementControlKind[]
+  >(
+    getDRPlacementControlResourceObj({
+      namespace: DISCOVERED_APP_NS,
+      selector: {
+        matchLabels: {
+          // To optimize the VM DRPC watch
+          [ODF_RESOURCE_TYPE_LABEL]: VirtualMachineModel.kind.toLowerCase(),
+        },
+      },
+    })
+  );
+
+  const drpc = React.useMemo(
+    () => findDRPCUsingVM(drpcs, vmName, vmNamespace),
+    [vmName, vmNamespace, drpcs]
+  );
+
+  if (!loaded || loadError || !drpc) return null;
+  return <DiscoveredParser application={drpc} />;
+};
+
+export const VirtualMachineParser: React.FC<VirtualMachineParserProps> = ({
+  virtualMachine,
+}) => {
+  const vmName = getName(virtualMachine);
+  const vmNamespace = getNamespace(virtualMachine);
+  const clusterName = virtualMachine?.['status']?.cluster;
+  const applicationSetName = getLabel(virtualMachine, KUBE_INSTANCE_LABEL);
+
+  // ACM search API call to find managed application resource
+  const searchQuery = React.useMemo(
+    () =>
+      queryManagedApplicationResourcesForVM(
+        [vmName, applicationSetName],
+        vmNamespace,
+        clusterName
+      ),
+    [vmName, vmNamespace, clusterName, applicationSetName]
+  );
+
+  const [searchResult] = useACMSafeFetch(searchQuery);
+
+  // 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 } = managedApplication || {};
+
+  if (kind === ArgoApplicationSetModel.kind) {
+    return <ApplicationSetHelper vmName={name} vmNamespace={namespace} />;
+  }
+
+  if (kind === ACMSubscriptionModel.kind) {
+    return (
+      <SubscriptionHelper
+        vmName={getLabelsFromSearchResult(managedApplication)?.app?.[0]}
+        vmNamespace={namespace}
+      />
+    );
+  }
+
+  return <DiscoveredHelper vmName={vmName} vmNamespace={vmNamespace} />;
+};
+
+type VirtualMachineParserProps = {
+  virtualMachine: K8sResourceCommon;
+};
+
+type ParserHelperProps = {
+  vmName: string;
+  vmNamespace: string;
+};
+
+export default VirtualMachineParser;
diff --git a/packages/mco/constants/doc.tsx b/packages/mco/constants/doc.tsx
index a95c22fc4..d2013b717 100644
--- a/packages/mco/constants/doc.tsx
+++ b/packages/mco/constants/doc.tsx
@@ -33,3 +33,13 @@ export const gettingStartedDRDocs = (mcoDocVersion) => ({
     mcoDocVersion
   )}#enable-dr-monitoring_monitor-dr`,
 });
+
+const odfDRDocTroubleshooting = (odfDocVersion) =>
+  `${odfDRDocHome(odfDocVersion)}/troubleshooting_disaster_recovery`;
+
+export const drStatusPopoverDocs = (mcoDocVersion: string) => ({
+  FAILOVER: `${odfDRDocTroubleshooting(mcoDocVersion)}#failover_statuses`,
+  RELOCATION: `${odfDRDocTroubleshooting(mcoDocVersion)}#relocation_statuses`,
+  CLEANUP: `${odfDRDocTroubleshooting(mcoDocVersion)}#cleaning_up_application_resources`,
+  VOLUME_SYNC_DELAY: `${odfDRDocTroubleshooting(mcoDocVersion)}#volumes_are_syncing_slower_than_usual`,
+});
diff --git a/plugins/mco/console-extensions.json b/plugins/mco/console-extensions.json
index 47fdf411a..a4ba90c40 100644
--- a/plugins/mco/console-extensions.json
+++ b/plugins/mco/console-extensions.json
@@ -172,5 +172,14 @@
         "$codeRef": "appManagePolicy.default"
       }
     }
+  },
+  {
+    "type": "acm.virtualmachine/list/column",
+    "properties": {
+      "header": "%plugin__odf-multicluster-console~DR Status%",
+      "cell": {
+        "$codeRef": "dataPolicyStatusPopover.default"
+      }
+    }
   }
 ]


Reviews:
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T10:58:08Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T11:00:56Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T11:04:53Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T11:07:02Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T11:07:18Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T11:07:46Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T11:11:22Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T11:12:33Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T11:13:24Z
user: login: TimothyAsirJeyasing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T12:13:34Z
user: login: TimothyAsirJeyasing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T12:35:13Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T12:44:13Z
user: login: TimothyAsirJeyasing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T13:19:07Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T14:17:03Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T14:17:40Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T14:17:51Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T14:18:36Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T14:23:12Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T14:28:26Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T14:30:16Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T14:30:28Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:43:19Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:44:40Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:46:02Z
user: login: bipuladh
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:46:11Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:47:20Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:48:08Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T09:48:48Z
user: login: TimothyAsirJeyasing
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T11:21:34Z
user: login: GowthamShanmugam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T04:26:04Z
user: login: GowthamShanmugam
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T06:09:13Z

Review comments:
diff_hunk: @@ -180,21 +180,25 @@ const HelpLink: React.FC<{
 
 const statusHelpLinks = {
   [DRStatus.Warning]: {
-    href: '', // TODO: Replace with the actual link
+    href: 'https://docs.redhat.com/en/documentation/red_hat_openshift_data_foundation/4.19/html/configuring_openshift_data_foundation_disaster_recovery_for_openshift_workloads/troubleshooting_disaster_recovery#volumes_are_sinking_slower_than_usual',
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: It's a static URL, it should be dynamic URL based on the ODF version
created_at: 2025-03-21T10:58:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -377,13 +383,24 @@ const getDRStatusDetails = ({
       return createStatus(
         DRStatus.WaitOnUserToCleanUp,
         t('Action needed'),
-        t(
-          'Clean up application resources on failed cluster {{cluster}} to start the replication.',
-          {
-            cluster:
-              phase === DRPCStatus.FailingOver ? targetCluster : primaryCluster,
-          }
-        ),
+        <>
+          {t(
+            'Clean up application resources on failed cluster {{cluster}} to start the replication.',
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: this cleanup message is wrong for the relocation cleanup, in relocation there both the clusters are healthy, and there is no failed cluster.
created_at: 2025-03-21T11:00:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -377,13 +383,24 @@ const getDRStatusDetails = ({
       return createStatus(
         DRStatus.WaitOnUserToCleanUp,
         t('Action needed'),
-        t(
-          'Clean up application resources on failed cluster {{cluster}} to start the replication.',
-          {
-            cluster:
-              phase === DRPCStatus.FailingOver ? targetCluster : primaryCluster,
-          }
-        ),
+        <>
+          {t(
+            'Clean up application resources on failed cluster {{cluster}} to start the replication.',
+            {
+              cluster:
+                phase === DRPCStatus.FailingOver
+                  ? targetCluster
+                  : primaryCluster,
+            }
+          )}
+          {statusLink && (
+            <HelpLink
+              href={statusLink.href}
+              t={t}
+              message={t(statusLink.message)}
+            />
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: what is this new change?why we are calling functional component from a switch case? i can see HelpLink was there already on DRStatusPopover component
created_at: 2025-03-21T11:04:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,117 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  K8S_RESOURCE_SELECTOR_LABEL_KEY,
+  KUBE_INSTANCE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import { ACMSubscriptionModel, ArgoApplicationSetModel } from '@odf/mco/models';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import { ApplicationKind, getLabel, getName, getNamespace } from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<any> = ({ name, namespace }) => {
+  const [resource] = useK8sWatchResource<ArgoApplicationSetKind>(
+    getApplicationSetResourceObj({ name, namespace })
+  );
+
+  return <ApplicationSetParser application={resource} />;
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
  return (`loaded && !loadError) ? <ApplicationSetParser application={resource} /> : null `;
```
created_at: 2025-03-21T11:07:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,117 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  K8S_RESOURCE_SELECTOR_LABEL_KEY,
+  KUBE_INSTANCE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import { ACMSubscriptionModel, ArgoApplicationSetModel } from '@odf/mco/models';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import { ApplicationKind, getLabel, getName, getNamespace } from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<any> = ({ name, namespace }) => {
+  const [resource] = useK8sWatchResource<ArgoApplicationSetKind>(
+    getApplicationSetResourceObj({ name, namespace })
+  );
+
+  return <ApplicationSetParser application={resource} />;
+};
+
+const SubscriptionHelper: React.FC<any> = (name, namespace) => {
+  const [resource] = useK8sWatchResource<ApplicationKind>(
+    getApplicationResourceObj({ name, namespace })
+  );
+
+  return <SubscriptionParser application={resource} />;
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
  return (`loaded && !loadError) ? <SubscriptionParser application={resource} /> : null `
```
created_at: 2025-03-21T11:07:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,117 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  K8S_RESOURCE_SELECTOR_LABEL_KEY,
+  KUBE_INSTANCE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import { ACMSubscriptionModel, ArgoApplicationSetModel } from '@odf/mco/models';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import { ApplicationKind, getLabel, getName, getNamespace } from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<any> = ({ name, namespace }) => {
+  const [resource] = useK8sWatchResource<ArgoApplicationSetKind>(
+    getApplicationSetResourceObj({ name, namespace })
+  );
+
+  return <ApplicationSetParser application={resource} />;
+};
+
+const SubscriptionHelper: React.FC<any> = (name, namespace) => {
+  const [resource] = useK8sWatchResource<ApplicationKind>(
+    getApplicationResourceObj({ name, namespace })
+  );
+
+  return <SubscriptionParser application={resource} />;
+};
+
+const DiscoveredHelper: React.FC<any> = ({ protectionName }) => {
+  const [drpc] = useK8sWatchResource<DRPlacementControlKind>(
+    getDRPlacementControlResourceObj({
+      namespace: DISCOVERED_APP_NS,
+      name: protectionName + '-drpc',
+    })
+  );
+
+  return <DiscoveredParser application={drpc} />;
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
  return (`loaded && !loadError) ? < DiscoveredParser application={resource} /> : null `;
```
created_at: 2025-03-21T11:07:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,117 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  K8S_RESOURCE_SELECTOR_LABEL_KEY,
+  KUBE_INSTANCE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import { ACMSubscriptionModel, ArgoApplicationSetModel } from '@odf/mco/models';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import { ApplicationKind, getLabel, getName, getNamespace } from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<any> = ({ name, namespace }) => {
+  const [resource] = useK8sWatchResource<ArgoApplicationSetKind>(
+    getApplicationSetResourceObj({ name, namespace })
+  );
+
+  return <ApplicationSetParser application={resource} />;
+};
+
+const SubscriptionHelper: React.FC<any> = (name, namespace) => {
+  const [resource] = useK8sWatchResource<ApplicationKind>(
+    getApplicationResourceObj({ name, namespace })
+  );
+
+  return <SubscriptionParser application={resource} />;
+};
+
+const DiscoveredHelper: React.FC<any> = ({ protectionName }) => {
+  const [drpc] = useK8sWatchResource<DRPlacementControlKind>(
+    getDRPlacementControlResourceObj({
+      namespace: DISCOVERED_APP_NS,
+      name: protectionName + '-drpc',
+    })
+  );
+
+  return <DiscoveredParser application={drpc} />;
+};
+
+export const VirtualMachineParser: React.FC<VirtualMachineParserProps> = ({
+  application: virtualMachine,
+}) => {
+  const vmName = getName(virtualMachine);
+  const vmNamespace = getNamespace(virtualMachine);
+  const clusterName = virtualMachine?.['status']?.cluster;
+  const applicationSetName = getLabel(virtualMachine, KUBE_INSTANCE_LABEL);
+  const protectionName = getLabel(
+    virtualMachine,
+    K8S_RESOURCE_SELECTOR_LABEL_KEY
+  );
+
+  // ACM search API call to find managed application resource
+  const searchQuery = React.useMemo(
+    () =>
+      queryManagedApplicationResourcesForVM(
+        [vmName, applicationSetName],
+        vmNamespace,
+        clusterName
+      ),
+    [vmName, vmNamespace, clusterName, applicationSetName]
+  );
+
+  const [searchResult] = useACMSafeFetch(searchQuery);
+
+  // 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 } = managedApplication || {};
+
+  // Conditional rendering based on the kind of managedApplication
+  if (kind === ArgoApplicationSetModel.kind) {
+    return <ApplicationSetHelper name={name} namespace={namespace} />;
+  }
+
+  if (kind === ACMSubscriptionModel.kind) {
+    return (
+      <SubscriptionHelper
+        name={getLabelsFromSearchResult(managedApplication)?.app?.[0]}
+        namespace={namespace}
+      />
+    );
+  }
+
+  // Fallback if no matching resource is found
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
```
its not fall, it is discovered. if you want to add fallback, you need to add if condition for discovered and then add fallback
created_at: 2025-03-21T11:11:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,6 +66,8 @@ export type DRPlacementControlKind = K8sResourceCommon & {
       // To identify all the kube objects that need DR protection.
       //  N/A for the managed  applications.
       kubeObjectSelector?: Selector;
+      // Recipe parameter definitions
+      recipeParameters?: Record<string, string[]>;
path: packages/mco/types/ramen.ts
user: login: GowthamShanmugam
body: are you using recipeParameters anywhere?
created_at: 2025-03-21T11:12:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -172,5 +172,14 @@
         "$codeRef": "appManagePolicy.default"
       }
     }
+  },
+  {
+    "type": "acm.virtualmachine/list/column",
+    "properties": {
+      "header": "%plugin__odf-multicluster-console~DR Status%",
path: plugins/mco/console-extensions.json
user: login: GowthamShanmugam
body: is this `DR Status` or `DR status`, please double check for managed and discovered list pages also.
created_at: 2025-03-21T11:13:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -377,13 +383,24 @@ const getDRStatusDetails = ({
       return createStatus(
         DRStatus.WaitOnUserToCleanUp,
         t('Action needed'),
-        t(
-          'Clean up application resources on failed cluster {{cluster}} to start the replication.',
-          {
-            cluster:
-              phase === DRPCStatus.FailingOver ? targetCluster : primaryCluster,
-          }
-        ),
+        <>
+          {t(
+            'Clean up application resources on failed cluster {{cluster}} to start the replication.',
+            {
+              cluster:
+                phase === DRPCStatus.FailingOver
+                  ? targetCluster
+                  : primaryCluster,
+            }
+          )}
+          {statusLink && (
+            <HelpLink
+              href={statusLink.href}
+              t={t}
+              message={t(statusLink.message)}
+            />
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: TimothyAsirJeyasing
body: yes its not required here, since its already available
created_at: 2025-03-21T12:13:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -172,5 +172,14 @@
         "$codeRef": "appManagePolicy.default"
       }
     }
+  },
+  {
+    "type": "acm.virtualmachine/list/column",
+    "properties": {
+      "header": "%plugin__odf-multicluster-console~DR Status%",
path: plugins/mco/console-extensions.json
user: login: TimothyAsirJeyasing
body: yes its DR Status only we are using in other places, checked
created_at: 2025-03-21T12:35:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -172,5 +172,14 @@
         "$codeRef": "appManagePolicy.default"
       }
     }
+  },
+  {
+    "type": "acm.virtualmachine/list/column",
+    "properties": {
+      "header": "%plugin__odf-multicluster-console~DR Status%",
path: plugins/mco/console-extensions.json
user: login: GowthamShanmugam
body: it should be `DR status` 
created_at: 2025-03-21T12:44:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -66,6 +66,8 @@ export type DRPlacementControlKind = K8sResourceCommon & {
       // To identify all the kube objects that need DR protection.
       //  N/A for the managed  applications.
       kubeObjectSelector?: Selector;
+      // Recipe parameter definitions
+      recipeParameters?: Record<string, string[]>;
path: packages/mco/types/ramen.ts
user: login: TimothyAsirJeyasing
body: its not used now, i will remove this
created_at: 2025-03-21T13:19:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,6 +20,22 @@ export const odfDeployExternalMode = (odfDocVersion) =>
     odfDocVersion
   )}/deploying_openshift_data_foundation_in_external_mode`;
 
+// Add functions for specific DR-related documentation sections
+export const odfDRDocTroubleshooting = (odfDocVersion) =>
+  `${odfDRDocHome(odfDocVersion)}/troubleshooting_disaster_recovery`;
+
+export const odfDRDocFailoverStatuses = (odfDocVersion) =>
+  `${odfDRDocTroubleshooting(odfDocVersion)}#failover_statuses`;
+
+export const odfDRDocRelocationStatuses = (odfDocVersion) =>
+  `${odfDRDocTroubleshooting(odfDocVersion)}#relocation_statuses`;
+
+export const odfDRDocCleaningUpResources = (odfDocVersion) =>
+  `${odfDRDocTroubleshooting(odfDocVersion)}#cleaning_up_application_resources`;
+
+export const odfDRDocVolumesSyncingSlowly = (odfDocVersion) =>
+  `${odfDRDocTroubleshooting(odfDocVersion)}#volumes_are_sinking_slower_than_usual`;
+
path: packages/shared/src/constants/doc.ts
user: login: GowthamShanmugam
body: ```suggestion


```
move this under MCO doc.tsx, and keep all these docs under drStatusPopoverDocs, Please check mco/constants/doc.tsx for more info
created_at: 2025-03-21T14:17:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,121 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  K8S_RESOURCE_SELECTOR_LABEL_KEY,
+  KUBE_INSTANCE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import { ACMSubscriptionModel, ArgoApplicationSetModel } from '@odf/mco/models';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import { ApplicationKind, getLabel, getName, getNamespace } from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<any> = ({ name, namespace }) => {
+  const [resource, loaded, loadError] =
+    useK8sWatchResource<ArgoApplicationSetKind>(
+      getApplicationSetResourceObj({ name, namespace })
+    );
+
+  return loaded && !loadError ? (
+    <ApplicationSetParser application={resource} />
+  ) : null;
+};
+
+const SubscriptionHelper: React.FC<any> = (name, namespace) => {
+  const [resource, loaded, loadError] = useK8sWatchResource<ApplicationKind>(
+    getApplicationResourceObj({ name, namespace })
+  );
+
+  return loaded && !loadError ? (
+    <SubscriptionParser application={resource} />
+  ) : null;
+};
+
+const DiscoveredHelper: React.FC<any> = ({ protectionName }) => {
+  const [drpc, loaded, loadError] = useK8sWatchResource<DRPlacementControlKind>(
+    getDRPlacementControlResourceObj({
+      namespace: DISCOVERED_APP_NS,
+      name: protectionName + '-drpc',
+    })
+  );
+
+  return loaded && !loadError ? <DiscoveredParser application={drpc} /> : null;
+};
+
+export const VirtualMachineParser: React.FC<VirtualMachineParserProps> = ({
+  application: virtualMachine,
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
  virtualMachine,
```
created_at: 2025-03-21T14:17:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,121 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  K8S_RESOURCE_SELECTOR_LABEL_KEY,
+  KUBE_INSTANCE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import { ACMSubscriptionModel, ArgoApplicationSetModel } from '@odf/mco/models';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import { ApplicationKind, getLabel, getName, getNamespace } from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<any> = ({ name, namespace }) => {
+  const [resource, loaded, loadError] =
+    useK8sWatchResource<ArgoApplicationSetKind>(
+      getApplicationSetResourceObj({ name, namespace })
+    );
+
+  return loaded && !loadError ? (
+    <ApplicationSetParser application={resource} />
+  ) : null;
+};
+
+const SubscriptionHelper: React.FC<any> = (name, namespace) => {
+  const [resource, loaded, loadError] = useK8sWatchResource<ApplicationKind>(
+    getApplicationResourceObj({ name, namespace })
+  );
+
+  return loaded && !loadError ? (
+    <SubscriptionParser application={resource} />
+  ) : null;
+};
+
+const DiscoveredHelper: React.FC<any> = ({ protectionName }) => {
+  const [drpc, loaded, loadError] = useK8sWatchResource<DRPlacementControlKind>(
+    getDRPlacementControlResourceObj({
+      namespace: DISCOVERED_APP_NS,
+      name: protectionName + '-drpc',
+    })
+  );
+
+  return loaded && !loadError ? <DiscoveredParser application={drpc} /> : null;
+};
+
+export const VirtualMachineParser: React.FC<VirtualMachineParserProps> = ({
+  application: virtualMachine,
+}) => {
+  const vmName = getName(virtualMachine);
+  const vmNamespace = getNamespace(virtualMachine);
+  const clusterName = virtualMachine?.['status']?.cluster;
+  const applicationSetName = getLabel(virtualMachine, KUBE_INSTANCE_LABEL);
+  const protectionName = getLabel(
+    virtualMachine,
+    K8S_RESOURCE_SELECTOR_LABEL_KEY
+  );
+
+  // ACM search API call to find managed application resource
+  const searchQuery = React.useMemo(
+    () =>
+      queryManagedApplicationResourcesForVM(
+        [vmName, applicationSetName],
+        vmNamespace,
+        clusterName
+      ),
+    [vmName, vmNamespace, clusterName, applicationSetName]
+  );
+
+  const [searchResult] = useACMSafeFetch(searchQuery);
+
+  // 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 } = managedApplication || {};
+
+  // Conditional rendering based on the kind of managedApplication
+  if (kind === ArgoApplicationSetModel.kind) {
+    return <ApplicationSetHelper name={name} namespace={namespace} />;
+  }
+
+  if (kind === ACMSubscriptionModel.kind) {
+    return (
+      <SubscriptionHelper
+        name={getLabelsFromSearchResult(managedApplication)?.app?.[0]}
+        namespace={namespace}
+      />
+    );
+  }
+
+  return <DiscoveredHelper protectionName={protectionName} />;
+};
+
+type VirtualMachineParserProps = {
+  application: K8sResourceCommon;
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
  virtualMachine: K8sResourceCommon;
```
created_at: 2025-03-21T14:17:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,121 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  K8S_RESOURCE_SELECTOR_LABEL_KEY,
+  KUBE_INSTANCE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import { ACMSubscriptionModel, ArgoApplicationSetModel } from '@odf/mco/models';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import { ApplicationKind, getLabel, getName, getNamespace } from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<any> = ({ name, namespace }) => {
+  const [resource, loaded, loadError] =
+    useK8sWatchResource<ArgoApplicationSetKind>(
+      getApplicationSetResourceObj({ name, namespace })
+    );
+
+  return loaded && !loadError ? (
+    <ApplicationSetParser application={resource} />
+  ) : null;
+};
+
+const SubscriptionHelper: React.FC<any> = (name, namespace) => {
+  const [resource, loaded, loadError] = useK8sWatchResource<ApplicationKind>(
+    getApplicationResourceObj({ name, namespace })
+  );
+
+  return loaded && !loadError ? (
+    <SubscriptionParser application={resource} />
+  ) : null;
+};
+
+const DiscoveredHelper: React.FC<any> = ({ protectionName }) => {
+  const [drpc, loaded, loadError] = useK8sWatchResource<DRPlacementControlKind>(
+    getDRPlacementControlResourceObj({
+      namespace: DISCOVERED_APP_NS,
+      name: protectionName + '-drpc',
+    })
+  );
+
+  return loaded && !loadError ? <DiscoveredParser application={drpc} /> : null;
+};
+
+export const VirtualMachineParser: React.FC<VirtualMachineParserProps> = ({
+  application: virtualMachine,
+}) => {
+  const vmName = getName(virtualMachine);
+  const vmNamespace = getNamespace(virtualMachine);
+  const clusterName = virtualMachine?.['status']?.cluster;
+  const applicationSetName = getLabel(virtualMachine, KUBE_INSTANCE_LABEL);
+  const protectionName = getLabel(
+    virtualMachine,
+    K8S_RESOURCE_SELECTOR_LABEL_KEY
+  );
+
+  // ACM search API call to find managed application resource
+  const searchQuery = React.useMemo(
+    () =>
+      queryManagedApplicationResourcesForVM(
+        [vmName, applicationSetName],
+        vmNamespace,
+        clusterName
+      ),
+    [vmName, vmNamespace, clusterName, applicationSetName]
+  );
+
+  const [searchResult] = useACMSafeFetch(searchQuery);
+
+  // 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 } = managedApplication || {};
+
+  // Conditional rendering based on the kind of managedApplication
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
```
created_at: 2025-03-21T14:18:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -164,37 +172,38 @@ const HelpLink: React.FC<{
   message: string;
 }> = ({ href, t, message }) => {
   return (
-    <Button
-      className="dr-status-help-link"
-      variant={ButtonVariant.link}
-      isInline
+    <Text
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: delete Text component, and `ViewDocumentation`
created_at: 2025-03-21T14:23:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,6 +1,13 @@
 import * as React from 'react';
 import { DRPCStatus, VolumeReplicationHealth } from '@odf/mco/constants';
 import { Progression } from '@odf/mco/types';
+import {
+  ODF_DEFAULT_DOC_VERSION,
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: ```suggestion
import { DOC_VERSION as mcoDocVersion } from '@odf/shared/hooks';

```
created_at: 2025-03-21T14:28:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -298,6 +307,22 @@ const getDRStatus = ({
   return DRStatus.Critical;
 };
 
+const getCleanupMessage = (
+  phase: DRPCStatus,
+  cluster: string,
+  t: TFunction
+) => {
+  return phase === DRPCStatus.FailingOver
+    ? t(
+        'Clean up application resources on failed cluster {{cluster}} to start the replication.',
+        { cluster }
+      )
+    : t(
+        'Clean up application resources on the source cluster {{cluster}} to complete the relocation.',
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: ```suggestion
        'Clean up application resources on the primary cluster {{cluster}} to complete the relocation.',
```
created_at: 2025-03-21T14:30:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -298,6 +307,22 @@ const getDRStatus = ({
   return DRStatus.Critical;
 };
 
+const getCleanupMessage = (
+  phase: DRPCStatus,
+  cluster: string,
+  t: TFunction
+) => {
+  return phase === DRPCStatus.FailingOver
+    ? t(
+        'Clean up application resources on failed cluster {{cluster}} to start the replication.',
+        { cluster }
+      )
+    : t(
+        'Clean up application resources on the source cluster {{cluster}} to complete the relocation.',
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: please discuss with UX about this change
created_at: 2025-03-21T14:30:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -158,43 +164,35 @@ const SyncStatus: React.FC<{
   );
 };
 
-const HelpLink: React.FC<{
-  href: string;
-  t: TFunction;
-  message: string;
-}> = ({ href, t, message }) => {
-  return (
-    <Button
-      className="dr-status-help-link"
-      variant={ButtonVariant.link}
-      isInline
-      href={href}
-      target="_blank"
-      rel="noopener noreferrer"
-      aria-label={t('Open documentation')}
-    >
-      {message}
-    </Button>
-  );
+const HelpLink: React.FC<{ href: string; t: TFunction; message: string }> = ({
+  href,
+  t,
+  message,
+}) => {
+  return <ViewDocumentation doclink={href} text={t(message)} padding="0" />;
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: ```suggestion
  return <ViewDocumentation doclink={href} text={message} padding="0" />;
```
translation only for the direct text like t("some text") not for variable like t(message)
created_at: 2025-03-24T09:43:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -158,43 +164,35 @@ const SyncStatus: React.FC<{
   );
 };
 
-const HelpLink: React.FC<{
-  href: string;
-  t: TFunction;
-  message: string;
-}> = ({ href, t, message }) => {
-  return (
-    <Button
-      className="dr-status-help-link"
-      variant={ButtonVariant.link}
-      isInline
-      href={href}
-      target="_blank"
-      rel="noopener noreferrer"
-      aria-label={t('Open documentation')}
-    >
-      {message}
-    </Button>
-  );
+const HelpLink: React.FC<{ href: string; t: TFunction; message: string }> = ({
+  href,
+  t,
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: ```suggestion
  
```
In each component, create a new t  instance instead of passing it down. Only pass t to functions when necessary.
created_at: 2025-03-24T09:44:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -298,6 +296,22 @@ const getDRStatus = ({
   return DRStatus.Critical;
 };
 
+const getCleanupMessage = (
+  phase: DRPCStatus,
+  cluster: string,
+  t: TFunction
+) => {
+  return phase === DRPCStatus.FailingOver
+    ? t(
+        'Clean up application resources on failed cluster {{cluster}} to start the replication.',
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: `to start the replication` is valid for Async, not for Sync
created_at: 2025-03-24T09:46:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,152 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  KUBE_INSTANCE_LABEL,
+  ODF_RESOURCE_TYPE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import {
+  ACMSubscriptionModel,
+  ApplicationKind,
+  ArgoApplicationSetModel,
+  getLabel,
+  getName,
+  getNamespace,
+  VirtualMachineModel,
+} from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import { findDRPCUsingVM } from '../../modals/app-manage-policies/utils/parser-utils';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<ParserHelperProps> = ({
+  vmName,
+  vmNamespace,
+}) => {
+  const [resource, loaded, loadError] =
+    useK8sWatchResource<ArgoApplicationSetKind>(
+      getApplicationSetResourceObj({ name: vmName, namespace: vmNamespace })
+    );
+
+  return loaded && !loadError ? (
+    <ApplicationSetParser application={resource} />
+  ) : null;
+};
+
+const SubscriptionHelper: React.FC<ParserHelperProps> = ({
+  vmName,
+  vmNamespace,
+}) => {
+  const [resource, loaded, loadError] = useK8sWatchResource<ApplicationKind>(
+    getApplicationResourceObj({ name: vmName, namespace: vmNamespace })
+  );
+
+  return loaded && !loadError ? (
+    <SubscriptionParser application={resource} />
+  ) : null;
+};
+
+const DiscoveredHelper: React.FC<ParserHelperProps> = ({
+  vmName,
+  vmNamespace,
+}) => {
+  const [drpcs, loaded, loadError] = useK8sWatchResource<
+    DRPlacementControlKind[]
+  >(
+    getDRPlacementControlResourceObj({
+      namespace: DISCOVERED_APP_NS,
+      selector: {
+        matchLabels: {
+          // To optimize the VM DRPC watch
+          [ODF_RESOURCE_TYPE_LABEL]: VirtualMachineModel.kind.toLowerCase(),
+        },
+      },
+    })
+  );
+
+  const drpc = React.useMemo(
+    () => findDRPCUsingVM(drpcs, vmName, vmNamespace),
+    [vmName, vmNamespace, drpcs]
+  );
+
+  return loaded && !loadError && drpc ? (
+    <DiscoveredParser application={drpc} />
+  ) : null;
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
 if (!loaded || loadError || !drpc) return null;

  return <DiscoveredParser application={drpc} />;
```
created_at: 2025-03-24T09:47:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,152 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  KUBE_INSTANCE_LABEL,
+  ODF_RESOURCE_TYPE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import {
+  ACMSubscriptionModel,
+  ApplicationKind,
+  ArgoApplicationSetModel,
+  getLabel,
+  getName,
+  getNamespace,
+  VirtualMachineModel,
+} from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import { findDRPCUsingVM } from '../../modals/app-manage-policies/utils/parser-utils';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<ParserHelperProps> = ({
+  vmName,
+  vmNamespace,
+}) => {
+  const [resource, loaded, loadError] =
+    useK8sWatchResource<ArgoApplicationSetKind>(
+      getApplicationSetResourceObj({ name: vmName, namespace: vmNamespace })
+    );
+
+  return loaded && !loadError ? (
+    <ApplicationSetParser application={resource} />
+  ) : null;
+};
+
+const SubscriptionHelper: React.FC<ParserHelperProps> = ({
+  vmName,
+  vmNamespace,
+}) => {
+  const [resource, loaded, loadError] = useK8sWatchResource<ApplicationKind>(
+    getApplicationResourceObj({ name: vmName, namespace: vmNamespace })
+  );
+
+  return loaded && !loadError ? (
+    <SubscriptionParser application={resource} />
+  ) : null;
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
  if (!loaded || loadError) return null;
  
  return <SubscriptionParser application={resource} />;
```
created_at: 2025-03-24T09:48:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,152 @@
+import * as React from 'react';
+import {
+  DISCOVERED_APP_NS,
+  HUB_CLUSTER_NAME,
+  KUBE_INSTANCE_LABEL,
+  ODF_RESOURCE_TYPE_LABEL,
+} from '@odf/mco/constants';
+import {
+  getApplicationResourceObj,
+  getApplicationSetResourceObj,
+  getDRPlacementControlResourceObj,
+  useACMSafeFetch,
+} from '@odf/mco/hooks';
+import {
+  ArgoApplicationSetKind,
+  DRPlacementControlKind,
+  SearchResultItemType,
+} from '@odf/mco/types';
+import {
+  getLabelsFromSearchResult,
+  queryManagedApplicationResourcesForVM,
+} from '@odf/mco/utils';
+import {
+  ACMSubscriptionModel,
+  ApplicationKind,
+  ArgoApplicationSetModel,
+  getLabel,
+  getName,
+  getNamespace,
+  VirtualMachineModel,
+} from '@odf/shared';
+import {
+  K8sResourceCommon,
+  useK8sWatchResource,
+} from '@openshift-console/dynamic-plugin-sdk';
+import { findDRPCUsingVM } from '../../modals/app-manage-policies/utils/parser-utils';
+import {
+  ApplicationSetParser,
+  SubscriptionParser,
+  DiscoveredParser,
+} from '../parsers';
+
+const ApplicationSetHelper: React.FC<ParserHelperProps> = ({
+  vmName,
+  vmNamespace,
+}) => {
+  const [resource, loaded, loadError] =
+    useK8sWatchResource<ArgoApplicationSetKind>(
+      getApplicationSetResourceObj({ name: vmName, namespace: vmNamespace })
+    );
+
+  return loaded && !loadError ? (
+    <ApplicationSetParser application={resource} />
+  ) : null;
path: packages/mco/components/dr-status-popover/parsers/virtualmachine-parser.tsx
user: login: GowthamShanmugam
body: ```suggestion
  if (!loaded || loadError) return null;
  
  return <ApplicationSetParser application={resource} />;
```
created_at: 2025-03-24T09:48:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -298,6 +296,22 @@ const getDRStatus = ({
   return DRStatus.Critical;
 };
 
+const getCleanupMessage = (
+  phase: DRPCStatus,
+  cluster: string,
+  t: TFunction
+) => {
+  return phase === DRPCStatus.FailingOver
+    ? t(
+        'Clean up application resources on failed cluster {{cluster}} to start the replication.',
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: TimothyAsirJeyasing
body: Sure will remove the word "to start the replication" for the consistency
created_at: 2025-03-24T11:21:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -158,44 +164,35 @@ const SyncStatus: React.FC<{
   );
 };
 
-const HelpLink: React.FC<{
-  href: string;
-  t: TFunction;
-  message: string;
-}> = ({ href, t, message }) => {
-  return (
-    <Button
-      className="dr-status-help-link"
-      variant={ButtonVariant.link}
-      isInline
-      href={href}
-      target="_blank"
-      rel="noopener noreferrer"
-      aria-label={t('Open documentation')}
-    >
-      {message}
-    </Button>
-  );
+const HelpLink: React.FC<{ href: string; message: string }> = ({
+  href,
+  message,
+}) => {
+  return <ViewDocumentation doclink={href} text={message} padding="0" />;
 };
path: packages/mco/components/dr-status-popover/dr-status-popover.tsx
user: login: GowthamShanmugam
body: Do we really need this new component `HelpLink `? what about calling `ViewDocumentation` directly
created_at: 2025-03-25T04:26:03Z
author_association: CONTRIBUTOR

PR comments:
user: login: TimothyAsirJeyasing
created_at: 2025-03-21T13:33:42Z
body: ![Screenshot from 2025-03-21 18-47-15](https://github.com/user-attachments/assets/944beee2-b263-4e30-a65a-5345fed70c12)

![Screenshot from 2025-03-21 18-47-01](https://github.com/user-attachments/assets/8f25562e-7136-4551-99fb-ae5a7c1ea265)

author_association: CONTRIBUTOR
user: login: bipuladh
created_at: 2025-03-24T09:46:21Z
body: /lgtm cancel
author_association: CONTRIBUTOR
user: login: GowthamShanmugam
created_at: 2025-03-24T10:15:44Z
body: /retitle [RHSTOR-7186](https://issues.redhat.com//browse/RHSTOR-7186): DR Status popover for virtual machine list page.
author_association: CONTRIBUTOR
user: login: openshift-ci-robot
created_at: 2025-03-24T10:15:51Z
body: @TimothyAsirJeyasing: This pull request references [RHSTOR-7186](https://issues.redhat.com//browse/RHSTOR-7186) which is a valid jira issue.

<details>

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

>https://issues.redhat.com/browse/RHSTOR-7186


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[bot]
created_at: 2025-03-25T06:09:19Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/red-hat-storage/odf-console/pull/1916#pullrequestreview-2712486386" title="Approved">GowthamShanmugam</a>*, *<a href="https://github.com/red-hat-storage/odf-console/pull/1916#" title="Author self-approved">TimothyAsirJeyasing</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]

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