Skip to content

Conversation

@cajieh
Copy link
Contributor

@cajieh cajieh commented Jan 19, 2026

Summary

Removes the withDashboardResources Higher-Order Component in favor of targeted React hooks, modernizing 17 components across 10 files as part of the "Remove and Update Firehose" epic.

Problem

The withDashboardResources HOC injected all capabilities into every component regardless of actual needs:

  • Performance: Unnecessary re-renders on unrelated data changes
  • Clarity: Unclear dependencies per component
  • Technical debt: Class-based HOC + Redux + Firehose coupling
  • Type safety: Complex HOC intersection types

Solution

Replace monolithic HOC with targeted hooks based on actual usage:

Screenshot 2026-01-22 at 1 56 02 PM

Key Change: useDynamicDashboardResources

Bridges declarative hooks with imperative plugin requirements:. Enables runtime resource watching without violating Rules of Hooks
const { watchResource, stopWatchResource, results } = useDynamicDashboardResources();

Plugin extensions can register 0-N resources at runtime

  extensions.forEach((ext, i) => {
    watchResource(key(i), ext.properties.k8sResource);
  });

Benefits

  • Performance: Components only watch what they need
  • Code quality: -271 lines, clearer intent
  • Type safety: Specific return types per hook
  • Modern React: Hooks over HOCs, functional components

Summary by CodeRabbit

  • Refactor
    • Modernized dashboard component data-fetching architecture across cluster and project dashboards, improving code organization and maintainability while preserving all existing functionality.

✏️ Tip: You can customize this high-level summary in your review settings.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jan 19, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 19, 2026

@cajieh: This pull request references CONSOLE-5031 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

NOT READY FOR READY

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

📝 Walkthrough

Walkthrough

This pull request refactors the console dashboard resource management from a higher-order component pattern (withDashboardResources) to a hook-based architecture. A new useDynamicK8sWatchResources hook provides imperative APIs for dynamically registering and unregistering Kubernetes resources at runtime. The useDashboardResources hook is updated to accept an additional parameter for URL fetching. Multiple dashboard and plugin components are refactored to use these hooks directly instead of receiving resource data through props injected by the HOC. The withDashboardResources export and its associated types are removed. Component public signatures are simplified by removing dependency on DashboardItemProps and shifting data loading into components via hooks.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is truncated and incomplete, ending with an ellipsis without conveying the full scope of this major architectural refactor that replaces the withDashboardResources HOC with hooks across 17 components. Complete the title to reflect the full change: 'Refactor withDashboardResources HOC into hook-based patterns' or similar, ensuring it communicates the main transformation without requiring external context.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jan 19, 2026
@openshift-ci openshift-ci bot added the component/core Related to console core functionality label Jan 19, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Jan 19, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: cajieh

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added component/dashboard Related to dashboard approved Indicates a PR has been approved by an approver from all required OWNERS files. component/metal3 Related to metal3-plugin component/shared Related to console-shared labels Jan 19, 2026
@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch 2 times, most recently from 17a6b03 to d56e111 Compare January 19, 2026 21:07
@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 19, 2026
@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch from d56e111 to faafcc4 Compare January 19, 2026 21:16
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 19, 2026
@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch from faafcc4 to 2fc0cfb Compare January 19, 2026 21:18
<RecentEvent />
<RecentEvents />
</ActivityBody>
<RecentEventFooter />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant!

@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch from 2fc0cfb to d11eec1 Compare January 19, 2026 21:33
<OngoingActivity projectName={projectName} />
<RecentEvent projectName={projectName} viewEvents={viewEvents} />
</ActivityBody>
<RecentEventFooter projectName={projectName} viewEvents={viewEvents} />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant after the refactoring! The footer functionality is preserved with the same logic:
const shouldShowFooter = events?.loaded && events?.data && events.data.length > 50;

@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch 4 times, most recently from 46dbf18 to 0fa4c50 Compare January 21, 2026 14:18
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 21, 2026

@cajieh: This pull request references CONSOLE-5031 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch 2 times, most recently from ff8cacf to 0538151 Compare January 21, 2026 15:39
@cajieh
Copy link
Contributor Author

cajieh commented Jan 22, 2026

/retest

@cajieh cajieh changed the title [WIP] CONSOLE-5031: Refactor withDashboardResources in with-dashboard-resources.tsx into … CONSOLE-5029: Refactor withDashboardResources in with-dashboard-resources.tsx into … Jan 22, 2026
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jan 22, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 22, 2026

@cajieh: This pull request references CONSOLE-5029 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 22, 2026

@cajieh: This pull request references CONSOLE-5029 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

Removes the withDashboardResources Higher-Order Component in favor of targeted React hooks, modernizing 17 components across 10 files as part of the "Remove and Update Firehose" epic.

Problem

The withDashboardResources HOC injected all capabilities into every component regardless of actual needs:

Performance: Unnecessary re-renders on unrelated data changes
Clarity: Unclear dependencies per component
Technical debt: Class-based HOC + Redux + Firehose coupling
Type safety: Complex HOC intersection types

Solution

Replace monolithic HOC with targeted hooks based on actual usage:

Screenshot 2026-01-22 at 1 56 02 PM

Key Change: useDynamicDashboardResources

Bridges declarative hooks with imperative plugin requirements:
// Enables runtime resource watching without violating Rules of Hooks
const { watchResource, stopWatchResource, results } = useDynamicDashboardResources();

// Plugin extensions can register 0-N resources at runtime

 extensions.forEach((ext, i) => {
   watchResource(key(i), ext.properties.k8sResource);
 });

Benefits

Performance: Components only watch what they need
Code quality: -271 lines, clearer intent
Type safety: Specific return types per hook
Modern React: Hooks over HOCs, functional components

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 22, 2026

@cajieh: This pull request references CONSOLE-5029 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

Removes the withDashboardResources Higher-Order Component in favor of targeted React hooks, modernizing 17 components across 10 files as part of the "Remove and Update Firehose" epic.

Problem

The withDashboardResources HOC injected all capabilities into every component regardless of actual needs:

  • Performance: Unnecessary re-renders on unrelated data changes
  • Clarity: Unclear dependencies per component
  • Technical debt: Class-based HOC + Redux + Firehose coupling
  • Type safety: Complex HOC intersection types

Solution

Replace monolithic HOC with targeted hooks based on actual usage:

Screenshot 2026-01-22 at 1 56 02 PM

Key Change: useDynamicDashboardResources

Bridges declarative hooks with imperative plugin requirements:
// Enables runtime resource watching without violating Rules of Hooks const { watchResource, stopWatchResource, results } = useDynamicDashboardResources();

 // Plugin extensions can register 0-N resources at runtime

 extensions.forEach((ext, i) => {
   watchResource(key(i), ext.properties.k8sResource);
 });

Benefits

  • Performance: Components only watch what they need
  • Code quality: -271 lines, clearer intent
  • Type safety: Specific return types per hook
  • Modern React: Hooks over HOCs, functional components

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 22, 2026

@cajieh: This pull request references CONSOLE-5029 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

Removes the withDashboardResources Higher-Order Component in favor of targeted React hooks, modernizing 17 components across 10 files as part of the "Remove and Update Firehose" epic.

Problem

The withDashboardResources HOC injected all capabilities into every component regardless of actual needs:

  • Performance: Unnecessary re-renders on unrelated data changes
  • Clarity: Unclear dependencies per component
  • Technical debt: Class-based HOC + Redux + Firehose coupling
  • Type safety: Complex HOC intersection types

Solution

Replace monolithic HOC with targeted hooks based on actual usage:

Screenshot 2026-01-22 at 1 56 02 PM

Key Change: useDynamicDashboardResources

Bridges declarative hooks with imperative plugin requirements:
// Enables runtime resource watching without violating Rules of Hooks const { watchResource, stopWatchResource, results } = useDynamicDashboardResources();

 // Plugin extensions can register 0-N resources at runtime

 extensions.forEach((ext, i) => {
   watchResource(key(i), ext.properties.k8sResource);
 });

Benefits

  • Performance: Components only watch what they need
  • Code quality: -271 lines, clearer intent
  • Type safety: Specific return types per hook
  • Modern React: Hooks over HOCs, functional components

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 23, 2026

@cajieh: This pull request references CONSOLE-5029 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

Removes the withDashboardResources Higher-Order Component in favor of targeted React hooks, modernizing 17 components across 10 files as part of the "Remove and Update Firehose" epic.

Problem

The withDashboardResources HOC injected all capabilities into every component regardless of actual needs:

  • Performance: Unnecessary re-renders on unrelated data changes
  • Clarity: Unclear dependencies per component
  • Technical debt: Class-based HOC + Redux + Firehose coupling
  • Type safety: Complex HOC intersection types

Solution

Replace monolithic HOC with targeted hooks based on actual usage:

Screenshot 2026-01-22 at 1 56 02 PM

Key Change: useDynamicDashboardResources

Bridges declarative hooks with imperative plugin requirements:
// Enables runtime resource watching without violating Rules of Hooks
const { watchResource, stopWatchResource, results } = useDynamicDashboardResources();

 // Plugin extensions can register 0-N resources at runtime

 extensions.forEach((ext, i) => {
   watchResource(key(i), ext.properties.k8sResource);
 });

Benefits

  • Performance: Components only watch what they need
  • Code quality: -271 lines, clearer intent
  • Type safety: Specific return types per hook
  • Modern React: Hooks over HOCs, functional components

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 23, 2026

@cajieh: This pull request references CONSOLE-5029 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

Removes the withDashboardResources Higher-Order Component in favor of targeted React hooks, modernizing 17 components across 10 files as part of the "Remove and Update Firehose" epic.

Problem

The withDashboardResources HOC injected all capabilities into every component regardless of actual needs:

  • Performance: Unnecessary re-renders on unrelated data changes
  • Clarity: Unclear dependencies per component
  • Technical debt: Class-based HOC + Redux + Firehose coupling
  • Type safety: Complex HOC intersection types

Solution

Replace monolithic HOC with targeted hooks based on actual usage:

Screenshot 2026-01-22 at 1 56 02 PM

Key Change: useDynamicDashboardResources

Bridges declarative hooks with imperative plugin requirements:
// Enables runtime resource watching without violating Rules of Hooks
const { watchResource, stopWatchResource, results } = useDynamicDashboardResources();

 // Plugin extensions can register 0-N resources at runtime

 extensions.forEach((ext, i) => {
   watchResource(key(i), ext.properties.k8sResource);
 });

Benefits

  • Performance: Components only watch what they need
  • Code quality: -271 lines, clearer intent
  • Type safety: Specific return types per hook
  • Modern React: Hooks over HOCs, functional components

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 23, 2026

@cajieh: This pull request references CONSOLE-5029 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

Removes the withDashboardResources Higher-Order Component in favor of targeted React hooks, modernizing 17 components across 10 files as part of the "Remove and Update Firehose" epic.

Problem

The withDashboardResources HOC injected all capabilities into every component regardless of actual needs:

  • Performance: Unnecessary re-renders on unrelated data changes
  • Clarity: Unclear dependencies per component
  • Technical debt: Class-based HOC + Redux + Firehose coupling
  • Type safety: Complex HOC intersection types

Solution

Replace monolithic HOC with targeted hooks based on actual usage:

Screenshot 2026-01-22 at 1 56 02 PM

Key Change: useDynamicDashboardResources

Bridges declarative hooks with imperative plugin requirements:
Enables runtime resource watching without violating Rules of Hooks
const { watchResource, stopWatchResource, results } = useDynamicDashboardResources();

Plugin extensions can register 0-N resources at runtime

 extensions.forEach((ext, i) => {
   watchResource(key(i), ext.properties.k8sResource);
 });

Benefits

  • Performance: Components only watch what they need
  • Code quality: -271 lines, clearer intent
  • Type safety: Specific return types per hook
  • Modern React: Hooks over HOCs, functional components

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 23, 2026

@cajieh: This pull request references CONSOLE-5029 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

Removes the withDashboardResources Higher-Order Component in favor of targeted React hooks, modernizing 17 components across 10 files as part of the "Remove and Update Firehose" epic.

Problem

The withDashboardResources HOC injected all capabilities into every component regardless of actual needs:

  • Performance: Unnecessary re-renders on unrelated data changes
  • Clarity: Unclear dependencies per component
  • Technical debt: Class-based HOC + Redux + Firehose coupling
  • Type safety: Complex HOC intersection types

Solution

Replace monolithic HOC with targeted hooks based on actual usage:

Screenshot 2026-01-22 at 1 56 02 PM

Key Change: useDynamicDashboardResources

Bridges declarative hooks with imperative plugin requirements:. Enables runtime resource watching without violating Rules of Hooks
const { watchResource, stopWatchResource, results } = useDynamicDashboardResources();

Plugin extensions can register 0-N resources at runtime

 extensions.forEach((ext, i) => {
   watchResource(key(i), ext.properties.k8sResource);
 });

Benefits

  • Performance: Components only watch what they need
  • Code quality: -271 lines, clearer intent
  • Type safety: Specific return types per hook
  • Modern React: Hooks over HOCs, functional components

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch from 0538151 to 0fd3120 Compare January 26, 2026 13:42
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 26, 2026

@cajieh: This pull request references CONSOLE-5029 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the spike to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

Removes the withDashboardResources Higher-Order Component in favor of targeted React hooks, modernizing 17 components across 10 files as part of the "Remove and Update Firehose" epic.

Problem

The withDashboardResources HOC injected all capabilities into every component regardless of actual needs:

  • Performance: Unnecessary re-renders on unrelated data changes
  • Clarity: Unclear dependencies per component
  • Technical debt: Class-based HOC + Redux + Firehose coupling
  • Type safety: Complex HOC intersection types

Solution

Replace monolithic HOC with targeted hooks based on actual usage:

Screenshot 2026-01-22 at 1 56 02 PM

Key Change: useDynamicDashboardResources

Bridges declarative hooks with imperative plugin requirements:. Enables runtime resource watching without violating Rules of Hooks
const { watchResource, stopWatchResource, results } = useDynamicDashboardResources();

Plugin extensions can register 0-N resources at runtime

 extensions.forEach((ext, i) => {
   watchResource(key(i), ext.properties.k8sResource);
 });

Benefits

  • Performance: Components only watch what they need
  • Code quality: -271 lines, clearer intent
  • Type safety: Specific return types per hook
  • Modern React: Hooks over HOCs, functional components

Summary by CodeRabbit

  • Refactor
  • Modernized dashboard component data-fetching architecture across cluster and project dashboards, improving code organization and maintainability while preserving all existing functionality.

✏️ Tip: You can customize this high-level summary in your review settings.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In
`@frontend/public/components/dashboard/dashboards-page/cluster-dashboard/activity-card.tsx`:
- Around line 130-148: The computed allPrometheusActivities currently calls
a.properties.isActivity without null-checking (isActivity is optional) and
recalculates identical queryResults in both filter and map; fix by iterating
prometheusActivities once inside the useMemo, for each activity compute
queryResults once from a.properties.queries using
prometheusResults.getIn([...,'data']) and then only call
a.properties.isActivity?.(queryResults) (or check typeof isActivity ===
'function') before including the activity in the result; return objects with
component: a.properties.component and results: queryResults for activities that
pass the check.

In
`@frontend/public/components/dashboard/dashboards-page/cluster-dashboard/details-card.tsx`:
- Around line 120-133: The fetch flow in the useEffect (fetchK8sVersion) is
storing the raw Response instead of parsed data and doesn't check response.ok;
update fetchK8sVersion to await fetch('version'), verify response.ok and throw
an error with status/text if not ok, then await response.json() (or
response.text() if the endpoint returns plain text) and pass the parsed value
into setK8sVersion; on errors call setK8sVersionError with a meaningful Error
(include status/message); keep references to flagPending(openshiftFlag) and the
dependency on openshiftFlag as-is.
- Around line 108-109: The state k8sVersion is incorrectly typed as Response and
therefore never receives parsed JSON; change the useState for
k8sVersion/setK8sVersion to the correct data shape (e.g., the parsed version
object/interface expected by getK8sGitVersion, not Response) and type
k8sVersionError appropriately (e.g., string | Error | undefined). In the fetch
logic that calls getK8sGitVersion, await response.json() and pass that parsed
object into setK8sVersion (or adjust getK8sGitVersion to return parsed JSON) so
components read the actual gitVersion field; update the related type/interface
used by k8sVersion to match the JSON shape.

In
`@frontend/public/components/dashboard/dashboards-page/cluster-dashboard/health-item.tsx`:
- Around line 231-236: The fallback hardcoded message in the healthState
assignment is not internationalized; update the expression that sets
SubsystemHealth (the healthState const) so the fallback message uses the
translation function t(), e.g. replace the raw string 'Health handler not
available' with t('health.handlerNotAvailable') or t('Health handler not
available') consistent with project i18n keys; ensure
subsystem.healthHandler?.(...) ?? { state: HealthState.NOT_AVAILABLE, message:
t(...) } so it matches how URLHealthItem uses t().
🧹 Nitpick comments (5)
frontend/packages/console-shared/src/components/dashboard/utilization-card/TopConsumerPopover.tsx (3)

116-158: Consider a more declarative approach for top5Data computation.

The imperative for loop with mutation works, but a functional approach would be cleaner and more consistent with React idioms. Also, top5Data is recomputed on every render since it's not memoized.

♻️ Suggested functional refactor
-    const top5Data = [];
-
-    const [data, error] = getPrometheusQueryResponse(prometheusResults, query);
-    const bodyData = getInstantVectorStats(data, metric);
-
-    if (k8sResource && consumerLoaded && !consumersLoadError) {
-      for (const d of bodyData) {
-        const consumerExists = consumerData.some(
-          (consumer) =>
-            getName(consumer) === d.metric[metric] &&
-            (model.namespaced ? getNamespace(consumer) === d.metric.namespace : true),
-        );
-        if (consumerExists) {
-          top5Data.push({ ...d, y: humanize(d.y).string });
-        }
-        if (top5Data.length === 5) {
-          break;
-        }
-      }
-    }
+    const [data, error] = getPrometheusQueryResponse(prometheusResults, query);
+    const bodyData = getInstantVectorStats(data, metric);
+
+    const top5Data = useMemo(() => {
+      if (!k8sResource || !consumerLoaded || consumersLoadError) {
+        return [];
+      }
+      return bodyData
+        .filter((d) =>
+          consumerData.some(
+            (consumer) =>
+              getName(consumer) === d.metric[metric] &&
+              (model.namespaced ? getNamespace(consumer) === d.metric.namespace : true),
+          ),
+        )
+        .slice(0, 5)
+        .map((d) => ({ ...d, y: humanize(d.y).string }));
+    }, [k8sResource, consumerLoaded, consumersLoadError, bodyData, consumerData, model, metric, humanize]);

129-133: Trailing comma in dependency array may cause issues in some build configurations.

While valid in modern JS/TS, the trailing comma after isOpen in the dependency array can occasionally cause issues with certain bundler configurations.

const prometheusQueries = useMemo(() => (isOpen ? [{ query, namespace }] : []), [
  query,
  namespace,
  isOpen,
]);

288-292: Unused props in PopoverBodyProps type.

The topConsumers and error props are defined in the type but are not used in the PopoverBody component signature (line 117). These appear to be remnants from the previous HOC-based implementation.

🧹 Clean up unused type properties
 type PopoverBodyProps = PopoverProps & {
-  topConsumers?: DataPoint[][];
-  error?: boolean;
   isOpen: boolean;
 };
frontend/public/components/dashboard/dashboards-page/cluster-dashboard/health-item.tsx (1)

95-151: healthStatuses computation should be memoized.

The healthStatuses array is computed on every render. With multiple operator subsystems, this could be expensive. Consider wrapping in useMemo.

♻️ Memoize healthStatuses
-  const healthStatuses = operatorSubsystems.map((o, index) => {
+  const healthStatuses = useMemo(() => operatorSubsystems.map((o, index) => {
     const operatorResources = o.resources.reduce((acc, r) => {
       acc[r.prop] = resources[uniqueResource(r, index).prop] || {};
       return acc;
     }, {});
     if (Object.keys(operatorResources).some((resource) => operatorResources[resource].loadError)) {
       return { health: HealthState.NOT_AVAILABLE };
     }
     if (Object.keys(operatorResources).some((resource) => !operatorResources[resource].loaded)) {
       return { health: HealthState.LOADING };
     }
     const operatorStatuses = o.getOperatorsWithStatuses(operatorResources);
     if (!operatorStatuses.length) {
       return { health: HealthState.OK };
     }
     const importantStatuses = getMostImportantStatuses(operatorStatuses);
     return {
       health: importantStatuses[0].status.health,
       count: importantStatuses.length,
     };
-  });
+  }), [operatorSubsystems, resources]);
frontend/public/components/dashboard/dashboards-page/cluster-dashboard/inventory-card.tsx (1)

39-56: Mapper loading error is swallowed silently.

The catch block only logs to console. Consider setting an error state to surface mapper load failures in the UI, or at minimum, ensure resolvedMapper is used as fallback (which it is on line 88).

Comment on lines 130 to 143
const allPrometheusActivities = useMemo(
() =>
prometheusActivities
.filter((a) => {
const queryResults = a.properties.queries.map(
(q) => prometheusResults.getIn([q, 'data']) as PrometheusResponse,
);
};
}, [
watchK8sResource,
stopWatchK8sResource,
watchPrometheus,
stopWatchPrometheusQuery,
resourceActivities,
prometheusActivities,
]);

const allResourceActivities = useMemo(
() =>
_.flatten(
resourceActivities.map((a, index) => {
const k8sResources = _.get(
resources,
[uniqueResource(a.properties.k8sResource, index).prop, 'data'],
[],
) as FirehoseResult['data'];
return k8sResources
.filter((r) => (a.properties.isActivity ? a.properties.isActivity(r) : true))
.map((r) => ({
resource: r,
timestamp: a.properties.getTimestamp ? a.properties.getTimestamp(r) : null,
component: a.properties.component,
}));
}),
),
[resourceActivities, resources],
);

const allPrometheusActivities = useMemo(
() =>
prometheusActivities
.filter((a) => {
const queryResults = a.properties.queries.map(
(q) => prometheusResults.getIn([q, 'data']) as PrometheusResponse,
);
return a.properties.isActivity(queryResults);
})
.map((a) => {
const queryResults = a.properties.queries.map(
(q) => prometheusResults.getIn([q, 'data']) as PrometheusResponse,
);
return {
component: a.properties.component,
results: queryResults,
};
}),
[prometheusActivities, prometheusResults],
);

const resourcesLoaded = useMemo(
() =>
resourceActivities.every((a, index) => {
const uniqueProp = uniqueResource(a.properties.k8sResource, index).prop;
return resources[uniqueProp]?.loaded || resources[uniqueProp]?.loadError;
}),
[resourceActivities, resources],
);

const queriesLoaded = useMemo(
() =>
prometheusActivities.every((a) =>
a.properties.queries.every(
(q) =>
prometheusResults.getIn([q, 'data']) || prometheusResults.getIn([q, 'loadError']),
),
),
[prometheusActivities, prometheusResults],
);

return (
<OngoingActivityBody
loaded={resourcesLoaded && queriesLoaded}
resourceActivities={allResourceActivities}
prometheusActivities={allPrometheusActivities}
/>
);
},
),
);

const RecentEventFooter = withDashboardResources(
({ watchK8sResource, stopWatchK8sResource, resources }) => {
const { t } = useTranslation();
useEffect(() => {
watchK8sResource(eventsResource);
return () => {
stopWatchK8sResource(eventsResource);
};
}, [watchK8sResource, stopWatchK8sResource]);

const events = resources.events as FirehoseResult<EventKind[]>;
const shouldShowFooter = events?.loaded && events?.data && events.data.length > 50;

if (!shouldShowFooter) {
return null;
}

return (
<>
<Divider />
<CardFooter>
<Link to={viewEvents} data-test="events-view-all-link">
{t('public~View all events')}
</Link>
</CardFooter>
</>
);
},
);
return a.properties.isActivity(queryResults);
})
.map((a) => {
const queryResults = a.properties.queries.map(
(q) => prometheusResults.getIn([q, 'data']) as PrometheusResponse,
);
return {
component: a.properties.component,
results: queryResults,
};
}),
[prometheusActivities, prometheusResults],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential null reference and duplicate computation in allPrometheusActivities.

Two issues:

  1. a.properties.isActivity(queryResults) (line 137) is called without checking if isActivity is defined, but the extension type shows it's optional (per DashboardsOverviewPrometheusActivity definition).
  2. queryResults is computed identically in both filter and map callbacks.
🐛 Fix null safety and optimize
   const allPrometheusActivities = useMemo(
     () =>
-      prometheusActivities
-        .filter((a) => {
-          const queryResults = a.properties.queries.map(
-            (q) => prometheusResults.getIn([q, 'data']) as PrometheusResponse,
-          );
-          return a.properties.isActivity(queryResults);
-        })
-        .map((a) => {
-          const queryResults = a.properties.queries.map(
-            (q) => prometheusResults.getIn([q, 'data']) as PrometheusResponse,
-          );
-          return {
-            component: a.properties.component,
-            results: queryResults,
-          };
-        }),
+      prometheusActivities
+        .map((a) => {
+          const queryResults = a.properties.queries.map(
+            (q) => prometheusResults.getIn([q, 'data']) as PrometheusResponse,
+          );
+          return { activity: a, queryResults };
+        })
+        .filter(({ activity, queryResults }) =>
+          activity.properties.isActivity ? activity.properties.isActivity(queryResults) : true,
+        )
+        .map(({ activity, queryResults }) => ({
+          component: activity.properties.component,
+          results: queryResults,
+        })),
     [prometheusActivities, prometheusResults],
   );
🤖 Prompt for AI Agents
In
`@frontend/public/components/dashboard/dashboards-page/cluster-dashboard/activity-card.tsx`
around lines 130 - 148, The computed allPrometheusActivities currently calls
a.properties.isActivity without null-checking (isActivity is optional) and
recalculates identical queryResults in both filter and map; fix by iterating
prometheusActivities once inside the useMemo, for each activity compute
queryResults once from a.properties.queries using
prometheusResults.getIn([...,'data']) and then only call
a.properties.isActivity?.(queryResults) (or check typeof isActivity ===
'function') before including the activity in the result; return objects with
component: a.properties.component and results: queryResults for activities that
pass the check.

Comment on lines +108 to +109
const [k8sVersion, setK8sVersion] = useState<Response>();
const [k8sVersionError, setK8sVersionError] = useState();
Copy link

@coderabbitai coderabbitai bot Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Type mismatch: k8sVersion should be the parsed JSON, not Response.

The state is typed as Response, but getK8sGitVersion (line 143) expects parsed JSON data containing version information, not a raw Response object. This will cause the version to never display correctly.

🐛 Fix fetch and type
-  const [k8sVersion, setK8sVersion] = useState<Response>();
+  const [k8sVersion, setK8sVersion] = useState<any>();

And update the fetch logic:

     const fetchK8sVersion = async () => {
       try {
-        const version = await fetch('version');
-        setK8sVersion(version);
+        const response = await fetch('version');
+        if (!response.ok) {
+          throw new Error(`HTTP error: ${response.status}`);
+        }
+        const version = await response.json();
+        setK8sVersion(version);
       } catch (error) {
         setK8sVersionError(error);
       }
     };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [k8sVersion, setK8sVersion] = useState<Response>();
const [k8sVersionError, setK8sVersionError] = useState();
const [k8sVersion, setK8sVersion] = useState<any>();
const [k8sVersionError, setK8sVersionError] = useState();
🤖 Prompt for AI Agents
In
`@frontend/public/components/dashboard/dashboards-page/cluster-dashboard/details-card.tsx`
around lines 108 - 109, The state k8sVersion is incorrectly typed as Response
and therefore never receives parsed JSON; change the useState for
k8sVersion/setK8sVersion to the correct data shape (e.g., the parsed version
object/interface expected by getK8sGitVersion, not Response) and type
k8sVersionError appropriately (e.g., string | Error | undefined). In the fetch
logic that calls getK8sGitVersion, await response.json() and pass that parsed
object into setK8sVersion (or adjust getK8sGitVersion to return parsed JSON) so
components read the actual gitVersion field; update the related type/interface
used by k8sVersion to match the JSON shape.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These CodeRabbitAI suggestions could be considered later, as they are beyond the scope of refactoring the withDashboardResources and Firehose components.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cajieh — Understood! Keeping this PR focused on the withDashboardResources and Firehose refactoring is the right call for maintainability and reviewability.

Would you like me to open a GitHub issue to track the k8sVersion type mismatch fix as a follow-up? That way it won't be lost, and someone can pick it up in a future PR. Just let me know!

Comment on lines +120 to +133
useEffect(() => {
if (flagPending(openshiftFlag)) {
return;
}
const fetchK8sVersion = async () => {
try {
const version = await fetch('version');
setK8sVersion(version);
} catch (error) {
setK8sVersionError(error);
}
const fetchK8sVersion = async () => {
try {
const version = await fetch('version');
setK8sVersion(version);
} catch (error) {
setK8sVersionError(error);
}
};
fetchK8sVersion();
}, [openshiftFlag, watchK8sResource, stopWatchK8sResource]);
const serviceLevelTitle = useServiceLevelTitle();
};
fetchK8sVersion();
}, [openshiftFlag]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Missing response status check and JSON parsing.

The fetch call doesn't check response.ok before using the response, and critically, doesn't parse the JSON body. The Response object is stored directly instead of the parsed data.

This is related to the type issue above - the entire fetch flow needs correction.

🤖 Prompt for AI Agents
In
`@frontend/public/components/dashboard/dashboards-page/cluster-dashboard/details-card.tsx`
around lines 120 - 133, The fetch flow in the useEffect (fetchK8sVersion) is
storing the raw Response instead of parsed data and doesn't check response.ok;
update fetchK8sVersion to await fetch('version'), verify response.ok and throw
an error with status/text if not ok, then await response.json() (or
response.text() if the endpoint returns plain text) and pass the parsed value
into setK8sVersion; on errors call setK8sVersionError with a meaningful Error
(include status/message); keep references to flagPending(openshiftFlag) and the
dependency on openshiftFlag as-is.

Comment on lines +231 to +236
const healthState: SubsystemHealth = subsystem.healthHandler?.(
queryResults,
t,
k8sResult,
infrastructure,
) ?? { state: HealthState.NOT_AVAILABLE, message: 'Health handler not available' };
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded English string violates i18n requirements.

The fallback message 'Health handler not available' is not wrapped in t() for translation, unlike the equivalent in URLHealthItem (line 176-177).

🌐 Apply i18n translation
   const healthState: SubsystemHealth = subsystem.healthHandler?.(
     queryResults,
     t,
     k8sResult,
     infrastructure,
-  ) ?? { state: HealthState.NOT_AVAILABLE, message: 'Health handler not available' };
+  ) ?? { state: HealthState.NOT_AVAILABLE, message: healthStateMessage(HealthState.NOT_AVAILABLE, t) };
🤖 Prompt for AI Agents
In
`@frontend/public/components/dashboard/dashboards-page/cluster-dashboard/health-item.tsx`
around lines 231 - 236, The fallback hardcoded message in the healthState
assignment is not internationalized; update the expression that sets
SubsystemHealth (the healthState const) so the fallback message uses the
translation function t(), e.g. replace the raw string 'Health handler not
available' with t('health.handlerNotAvailable') or t('Health handler not
available') consistent with project i18n keys; ensure
subsystem.healthHandler?.(...) ?? { state: HealthState.NOT_AVAILABLE, message:
t(...) } so it matches how URLHealthItem uses t().

@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch 2 times, most recently from fc73419 to 26f594d Compare January 26, 2026 15:21
Comment on lines 58 to 65
const events = useMemo(
() => ({
data: eventsData,
loaded: eventsLoaded,
loadError: eventsLoadError,
}),
[eventsData, eventsLoaded, eventsLoadError],
);
Copy link
Member

@logonoff logonoff Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we need to adapt k8sWatchResource to FirehoseResult here and in a few other places.

Can we instead modify RecentEventsBody to directly accept these 3 props instead? This would also remove the FirehoseResult type from that component

@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch from 26f594d to 6e30068 Compare January 27, 2026 13:34
@openshift-ci openshift-ci bot added component/sdk Related to console-plugin-sdk plugin-api-changed Categorizes a PR as containing plugin API changes labels Jan 27, 2026
@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch from 6e30068 to e757ba3 Compare January 27, 2026 16:03
@cajieh
Copy link
Contributor Author

cajieh commented Jan 27, 2026

/retest

@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch from e757ba3 to a62f4ae Compare January 27, 2026 20:17
@cajieh
Copy link
Contributor Author

cajieh commented Jan 28, 2026

/test e2e-gcp-console

@cajieh cajieh force-pushed the refactor-withDashboard-resources-component branch from a62f4ae to 62151c4 Compare January 28, 2026 17:26
@cajieh
Copy link
Contributor Author

cajieh commented Jan 28, 2026

/test e2e-gcp-console

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Jan 28, 2026

@cajieh: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-gcp-console 62151c4 link true /test e2e-gcp-console

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. component/core Related to console core functionality component/dashboard Related to dashboard component/metal3 Related to metal3-plugin component/sdk Related to console-plugin-sdk component/shared Related to console-shared jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. plugin-api-changed Categorizes a PR as containing plugin API changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants