Skip to content

Conversation

@jkilzi
Copy link
Collaborator

@jkilzi jkilzi commented Jan 8, 2026

  • Added a new ClusterSizingWizard component to guide users through selecting migration preferences and reviewing cluster sizing recommendations.
  • Implemented a two-step wizard that collects user inputs for worker node configurations and displays calculated requirements based on VMware inventory data.
  • Created supporting components including SizingInputForm, SizingResult, and their respective footers for navigation.
  • Integrated mock data for development and testing purposes, with a plan for future API integration.

This feature enhances the user experience by providing tailored cluster sizing recommendations, facilitating smoother migrations to OpenShift.

Signed-off-by: Jonathan Kilzi jkilzi@redhat.com

Summary by CodeRabbit

  • New Features

    • Cluster Sizing Wizard added to the Report page — two-step flow to configure migration preferences and review/copy target cluster recommendations.
  • Changes

    • Sizing calculations now call the live backend API (replacing runtime mocks).
    • Default overcommit ratio reduced from 6 to 4.
    • UI: improved form controls, help popovers, and safer handling of optional result fields.
  • Documentation

    • Added detailed Cluster Sizer specification and usage guide.

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Adds a two-step Cluster Sizer wizard to Report that collects migration preferences, calls AssessmentApi.calculateAssessmentClusterRequirements via DI, displays recommendations, introduces UI types/constants, and keeps mocks for tests while removing runtime mock usage.

Changes

Cohort / File(s) Summary
Cluster Sizer Wizard & integration
src/pages/report/cluster-sizer/ClusterSizingWizard.tsx, src/pages/report/Report.tsx, src/pages/report/cluster-sizer/index.ts
New ClusterSizingWizard component added and wired into Report; opens from a new button and obtains AssessmentApi via DI to call calculateAssessmentClusterRequirements. ClusterSizingWizard exported as named + default.
Input & Result UI
src/pages/report/cluster-sizer/SizingInputForm.tsx, src/pages/report/cluster-sizer/SizingResult.tsx
New SizingInputForm (migration preferences) and SizingResult (recommendations) components; result includes loading/error handling and null-safe rendering; copy-to-clipboard text updated (removed hardcoded HA text).
Wizard Footers
src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx, src/pages/report/cluster-sizer/SizingResultWizardStepFooter.tsx
New footer components to trigger async calculation, navigate steps, and close the wizard.
Helpers & UI bits
src/pages/report/cluster-sizer/PopoverIcon.tsx
New reusable PopoverIcon component for inline help/tooltips.
Types & Constants
src/pages/report/cluster-sizer/types.ts, src/pages/report/cluster-sizer/constants.ts
New UI types and helpers; re-exports API models from @migration-planner-ui/api-client/models; default overcommitRatio changed from 64.
Mocks for tests/dev
src/pages/report/cluster-sizer/mocks/*
Adds mock request/response factories and data generation (clusterRequirementsRequest.mock.ts, clusterRequirementsResponse.mock.ts, data.mock.ts, mocks/index.ts) retained for tests; runtime now uses real API via DI.
Docs & plans
specs/cluster-sizer.md, .cursor/plans/cluster_sizer_api_integration_4747313e.plan.md
New specification and plan documenting API shapes, UX flow, and implementation notes.
Metadata
package.json, .gitignore
Bumps @migration-planner-ui/api-client to ^0.0.37; adjusts .gitignore to allow tracking .cursor/plans/*.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Report as Report.tsx
    participant Wizard as ClusterSizingWizard
    participant API as AssessmentApi
    participant Result as SizingResult

    User->>Report: Click "View target cluster recommendations"
    Report->>Wizard: open(assessmentId, clusterId, clusterName)
    Wizard->>Wizard: render Step 1 (SizingInputForm)
    User->>Wizard: submit preferences
    Wizard->>API: calculateAssessmentClusterRequirements(assessmentId, request)
    API-->>Wizard: ClusterRequirementsResponse
    Wizard->>Result: render Step 2 with sizerOutput
    User->>Result: review / copy / close
    User->>Wizard: close
    Wizard-->>Report: close modal
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

lgtm, approved

Suggested reviewers

  • machacekondra
  • ammont82
  • tupyy
  • ronenav

Poem

🐇 I hopped from mocks to API so bright,

Collected prefs, then sized each node just right,
Ratios tuned and numbers tallied true,
Recommendations ready — here for you!
A rabbit cheers: "Good clusters, through and through!"

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: introducing a Cluster Sizer feature for OpenShift cluster recommendations, with reference to the ticket ECOPROJECT-3706.
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

🧹 Recent nitpick comments
specs/cluster-sizer.md (2)

37-39: Add language specifier to fenced code block.

The endpoint code block is missing a language identifier. This helps with proper rendering and syntax highlighting.

Proposed fix
-```
+```http
 POST /api/v1/assessments/{id}/cluster-requirements
</details>

---

`77-86`: **Add language specifier to file structure code block.**

The file structure code block should have a language identifier (e.g., `text` or `plaintext`) to satisfy markdownlint.


<details>
<summary>Proposed fix</summary>

```diff
-```
+```text
 src/pages/report/cluster-sizer/
 ├── index.ts                  # Re-exports
src/pages/report/cluster-sizer/ClusterSizingWizard.tsx (1)

56-95: Use AbortController with useRef to handle modal closure during async API calls.

The concern about state updates on an unmounted component is valid. If the user closes the modal while calculateAssessmentClusterRequirements is in flight, the subsequent state updates (setSizerOutput, setError, setIsLoading) will trigger React warnings.

The API client supports AbortController signals (see src/pages/assessment/hooks/useRVToolsJob.ts for an existing pattern in the codebase). However, the proposed fix should use useRef to persist the AbortController across renders, and include a cleanup effect for component unmount:

Improved implementation using useRef pattern from codebase
+ const abortControllerRef = useRef<AbortController | null>(null);
+
  const handleCalculate = useCallback(async () => {
+   // Abort any previous in-flight request
+   if (abortControllerRef.current) {
+     abortControllerRef.current.abort();
+   }
+   const abortController = new AbortController();
+   abortControllerRef.current = abortController;
    setIsLoading(true);
    setError(null);

    try {
      // ... existing code ...
      const result = await assessmentApi.calculateAssessmentClusterRequirements(
        {
          id: assessmentId,
          clusterRequirementsRequest,
        },
+       { signal: abortController.signal },
      );

+     if (!abortController.signal.aborted) {
        setSizerOutput(result);
+     }
    } catch (err) {
+     if (abortController.signal.aborted) return;
      setError(
        err instanceof Error ? err : new Error('Failed to calculate sizing'),
      );
    } finally {
+     if (!abortController.signal.aborted) {
        setIsLoading(false);
+     }
    }
  }, [assessmentApi, assessmentId, clusterId, formValues]);

Also add a cleanup effect to abort pending requests when the modal closes:

+ useEffect(() => {
+   return () => {
+     if (abortControllerRef.current) {
+       abortControllerRef.current.abort();
+     }
+   };
+ }, []);
src/pages/report/cluster-sizer/SizingResult.tsx (2)

30-38: Consider reusing or consolidating formatNumber utilities.

There's already a formatNumber utility in src/utils/formatters.ts (lines 7-15) that formats large numbers with K/M suffixes. This local version uses toLocaleString() for thousands separators. Both approaches are valid but serve different purposes.

If locale-aware formatting is intentional here (for precise values), consider renaming this helper to formatNumberLocale to distinguish it from the existing utility that abbreviates large numbers.


97-103: Consider adding user feedback for clipboard operations.

The copy handler silently succeeds or fails without any visual feedback to the user. Consider:

  1. Showing a brief success indicator (e.g., changing button text to "Copied!" temporarily)
  2. Displaying an error notification on failure instead of just logging to console

This improves UX by confirming the action completed successfully.


📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a160afc and a45156c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • .cursor/plans/cluster_sizer_api_integration_4747313e.plan.md
  • .gitignore
  • package.json
  • specs/cluster-sizer.md
  • specs/cluster-switcher.md
  • src/pages/report/Report.tsx
  • src/pages/report/cluster-sizer/ClusterSizingWizard.tsx
  • src/pages/report/cluster-sizer/PopoverIcon.tsx
  • src/pages/report/cluster-sizer/SizingInputForm.tsx
  • src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/SizingResult.tsx
  • src/pages/report/cluster-sizer/SizingResultWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/constants.ts
  • src/pages/report/cluster-sizer/index.ts
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts
  • src/pages/report/cluster-sizer/mocks/data.mock.ts
  • src/pages/report/cluster-sizer/mocks/index.ts
  • src/pages/report/cluster-sizer/types.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/pages/report/cluster-sizer/index.ts
  • src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/mocks/data.mock.ts
  • src/pages/report/cluster-sizer/mocks/index.ts
  • src/pages/report/Report.tsx
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts
  • src/pages/report/cluster-sizer/types.ts
  • .gitignore
  • package.json
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-05T07:49:36.795Z
Learnt from: ammont82
Repo: kubev2v/migration-planner-ui-app PR: 248
File: src/pages/report/assessment-report/ClustersOverview.tsx:0-0
Timestamp: 2025-12-05T07:49:36.795Z
Learning: In src/pages/report/assessment-report/ClustersOverview.tsx, the CPU overcommitment view uses TOP_N=5 to show the top 5 clusters individually without aggregating remaining values, while vmByCluster and dataCenterDistribution views use TOP_N=4 and then add a "Rest of..." aggregated category, resulting in 5 total displayed items across all views but with different aggregation strategies.

Applied to files:

  • src/pages/report/cluster-sizer/SizingInputForm.tsx
  • src/pages/report/cluster-sizer/SizingResult.tsx
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts
  • .cursor/plans/cluster_sizer_api_integration_4747313e.plan.md
  • src/pages/report/cluster-sizer/ClusterSizingWizard.tsx
  • src/pages/report/cluster-sizer/constants.ts
  • specs/cluster-sizer.md
🧬 Code graph analysis (4)
src/pages/report/cluster-sizer/PopoverIcon.tsx (1)
src/components/SVGIcon.tsx (1)
  • SVGIconProps (3-8)
src/pages/report/cluster-sizer/SizingInputForm.tsx (3)
src/pages/report/cluster-sizer/types.ts (2)
  • SizingFormValues (44-57)
  • OvercommitRatio (34-34)
src/pages/report/cluster-sizer/index.ts (1)
  • SizingInputForm (2-2)
src/pages/report/cluster-sizer/constants.ts (3)
  • CPU_OPTIONS (38-44)
  • MEMORY_OPTIONS (49-55)
  • OVERCOMMIT_OPTIONS (60-91)
src/pages/report/cluster-sizer/SizingResult.tsx (4)
src/pages/report/cluster-sizer/types.ts (1)
  • SizingFormValues (44-57)
src/utils/formatters.ts (1)
  • formatNumber (8-16)
src/pages/report/cluster-sizer/constants.ts (1)
  • OVERCOMMIT_OPTIONS (60-91)
src/pages/report/cluster-sizer/index.ts (1)
  • SizingResult (3-3)
src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts (1)
src/pages/report/cluster-sizer/types.ts (4)
  • ClusterRequirementsResponse (18-18)
  • ClusterSizing (19-19)
  • InventoryTotals (20-20)
  • SizingResourceConsumption (22-22)
🪛 markdownlint-cli2 (0.18.1)
specs/cluster-sizer.md

37-37: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


77-77: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (14)
src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts (3)

1-12: LGTM!

Good documentation header with API endpoint reference and the imports are correctly typed. The previous issue with ResourceConsumption vs SizingResourceConsumption has been properly addressed.


14-193: LGTM!

Comprehensive set of mock data covering various cluster scenarios. Node counts are consistent across all mocks (totalNodes = workerNodes + controlPlaneNodes), and the values appropriately represent their described scenarios (small dev cluster, large enterprise, high over-commit, etc.).


195-247: LGTM!

Factory functions are well-implemented with consistent defaults matching mockClusterRequirementsResponse. Good use of nullish coalescing and the composition pattern in createMockClusterRequirementsResponse provides flexibility for test authors.

src/pages/report/cluster-sizer/PopoverIcon.tsx (1)

19-48: LGTM!

The component implementation is clean and follows PatternFly patterns correctly. The preventDefault on click appropriately handles the popover trigger behavior.

src/pages/report/cluster-sizer/SizingInputForm.tsx (2)

29-66: LGTM!

The handler implementations are clean with proper immutable state updates. Since values come from controlled FormSelect options with predefined constants, the parseInt usage is safe.


68-193: LGTM!

The form structure is well-organized with proper accessibility attributes (aria-labels, fieldIds). PatternFly form components are used correctly, and the PopoverIcon integration provides helpful context for each field.

src/pages/report/cluster-sizer/SizingResultWizardStepFooter.tsx (1)

16-41: LGTM!

The footer component correctly uses PatternFly's useWizardContext for navigation and follows the expected button layout from the spec (Close primary + Back link).

src/pages/report/cluster-sizer/ClusterSizingWizard.tsx (2)

38-45: LGTM!

State management is well-structured with appropriate types. The DI pattern for obtaining the AssessmentApi client follows project conventions.


97-147: LGTM!

The render structure correctly implements the two-step wizard flow with proper Modal accessibility attributes. The early return pattern for !isOpen is appropriate.

src/pages/report/cluster-sizer/SizingResult.tsx (1)

81-95: LGTM - Well-structured component with proper memoization.

Good use of useMemo for the plain text recommendation and useCallback for the copy handler. The dependency arrays are correct, and the component properly handles loading, error, and empty states.

.cursor/plans/cluster_sizer_api_integration_4747313e.plan.md (1)

1-23: Verify if .cursor/ directory should be committed to the repository.

This appears to be a Cursor AI IDE-specific plan file. Consider whether these internal development/planning artifacts should be:

  1. Added to .gitignore to keep the repository clean
  2. Moved to a dedicated documentation directory if intended to be shared

If this is intentionally committed as project documentation, it's well-structured and provides good context for the implementation.

src/pages/report/cluster-sizer/constants.ts (3)

141-148: Verify default custom CPU/memory combination validity.

The default customCpu: 32 is in CPU_OPTIONS, but customMemoryGb: 32 exists in MEMORY_OPTIONS. However, this creates a configuration with 32 CPU / 32 GB which seems imbalanced (typically 1-4 GB per CPU core is common).

Was this intentional, or should customMemoryGb default to a higher value like 64 or 128 GB for a 32 CPU configuration?


1-33: LGTM - Well-organized constants with proper typing.

The worker node presets, options arrays, and form labels are well-structured with clear descriptions. The type imports from ./types ensure type safety across the module.


150-164: LGTM - Validation constraints align with options.

The WORKER_NODE_CONSTRAINTS properly define min/max bounds that align with CPU_OPTIONS (max 200) and MEMORY_OPTIONS (max 512). The step values (2 for CPU, 4 for memory) are sensible for the input controls.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@jkilzi jkilzi requested review from ammont82 and ronenav January 8, 2026 19:28
@jkilzi jkilzi force-pushed the ECOPROJECT-3706 branch 6 times, most recently from d331ab5 to 47f2f0f Compare January 14, 2026 15:01
@jkilzi jkilzi marked this pull request as ready for review January 14, 2026 15:02
@openshift-ci openshift-ci bot requested review from machacekondra and tupyy January 14, 2026 15:02
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 @.gitignore:
- Around line 5-6: Replace the current ignore/negation pair so Git still
traverses .cursor to allow negation: stop ignoring the directory itself by
changing ".cursor/" to ".cursor/*" and add explicit negations for the plans
directory and its contents (e.g. add "!.cursor/plans/" and "!.cursor/plans/**")
so that .cursor/* is ignored but .cursor/plans/ and all files under it are
tracked.

In `@src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts`:
- Around line 7-10: Import fails because OvercommitRatioString is not exported
from types.ts; add a string-format type export to types.ts so the mock can
import it. In src/pages/report/cluster-sizer/types.ts export a new type named
OvercommitRatioString with the API string literals '1:1' | '1:2' | '1:4' |
'1:6'; keep the existing numeric OvercommitRatio type and, if necessary, add a
small comment or mapping helper (e.g., between OvercommitRatio and
OvercommitRatioString) so consumers know how to convert, then re-run the build
to ensure the mock import of OvercommitRatioString resolves.

In `@src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts`:
- Around line 7-12: The import list incorrectly references ResourceConsumption;
replace it with SizingResourceConsumption in the import statement and update any
type annotations/usages in createMockResourceConsumption to use
SizingResourceConsumption (e.g., change function return type and any
variable/type references inside createMockResourceConsumption and any related
mocks that expect ResourceConsumption) so they match the exported type name
SizingResourceConsumption.

In `@src/pages/report/cluster-sizer/SizingResult.tsx`:
- Around line 72-73: The two bullet lines in SizingResult.tsx are using
inconsistent bullet characters ("~" for CPU and "-" for Memory) causing
mismatched UI and text; update the template strings and any rendered JSX that
produce these lines (references: the template lines containing "CPU Over-Commit
Ratio: ${formatRatio(cpuOverCommitRatio)}" and "Memory Over-Commit Ratio:
${formatRatio(memoryOverCommitRatio)}" and the rendered UI block around lines
219-224) to use the same bullet character (pick either "~" or "-") for both
entries so the plain text recommendation and rendered UI match.
🧹 Nitpick comments (6)
src/pages/report/cluster-sizer/PopoverIcon.tsx (1)

9-17: Consider broadening IconComponent type to accept functional components.

React.ComponentClass<SVGIconProps> excludes functional components. PatternFly icons can be either class or function components depending on version. Consider using React.ComponentType<SVGIconProps> instead.

Suggested fix
 type PopoverIconProps = PopoverProps & {
   variant?: ButtonProps['variant'];
   component?: ButtonProps['component'];
-  IconComponent?: React.ComponentClass<SVGIconProps>;
+  IconComponent?: React.ComponentType<SVGIconProps>;
   noVerticalAlign?: boolean;
   buttonClassName?: string;
   buttonOuiaId?: string;
   buttonStyle?: React.CSSProperties;
 };
specs/cluster-sizer.md (2)

36-39: Add language specifier to fenced code block.

Per markdown linting, fenced code blocks should have a language specified. For endpoint paths, use text or http.

Suggested fix
 ### Endpoint
-```
+```text
 POST /api/v1/assessments/{id}/cluster-requirements
 ```

76-86: Add language specifier and verify file structure accuracy.

Two issues:

  1. The fenced code block should have a language specified (e.g., text).
  2. Line 85 references mockData.ts, but the PR introduces a mocks/ folder with index.ts and separate mock files. Consider updating to reflect the actual structure.
Suggested fix
 ## File Structure
-```
+```text
 src/pages/report/cluster-sizer/
 ├── index.ts                  # Re-exports
 ├── types.ts                  # TypeScript interfaces and type definitions
 ├── constants.ts              # Form options (CPU, memory, over-commit dropdowns)
 ├── ClusterSizingWizard.tsx   # Main wizard modal component
 ├── SizingInputForm.tsx       # Step 1: Migration preferences form
 ├── SizingResult.tsx          # Step 2: Results display
-└── mockData.ts               # Mock API responses for development
+├── PopoverIcon.tsx           # Popover helper component
+└── mocks/                    # Mock API responses for development
+    ├── index.ts
+    ├── clusterRequirementsRequest.mock.ts
+    └── clusterRequirementsResponse.mock.ts
 ```
src/pages/report/Report.tsx (1)

381-387: Consider handling the undefined id case more explicitly.

The id || '' fallback silently passes an empty string to the API if id is undefined. While the early returns (lines 80-120) should prevent this scenario, an empty assessmentId would cause the API call to fail.

Consider asserting that id exists or adding a guard:

{id && (
  <ClusterSizingWizard
    isOpen={isSizingWizardOpen}
    onClose={() => setIsSizingWizardOpen(false)}
    clusterName={clusterView.selectionLabel}
    clusterId={selectedClusterId}
    assessmentId={id}
  />
)}
src/pages/report/cluster-sizer/SizingResult.tsx (2)

97-103: Consider adding user feedback for copy action.

The copy handler silently succeeds or fails. Users have no indication whether the copy worked. A common pattern is to temporarily change the button text to "Copied!" or show a toast notification.

Example enhancement
const [copied, setCopied] = useState(false);

const handleCopyRecommendations = useCallback(async () => {
  try {
    await navigator.clipboard.writeText(plainTextRecommendation);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  } catch (err) {
    console.error('Failed to copy recommendations:', err);
  }
}, [plainTextRecommendation]);

// In the button:
<Button ...>
  {copied ? 'Copied!' : 'Copy recommendations'}
</Button>

123-135: Consider using PatternFly Alert for error display.

The error state uses plain text. Using a PatternFly Alert component with variant="danger" would provide better visual consistency and accessibility.

Optional enhancement
import { Alert } from '@patternfly/react-core';

if (error) {
  return (
    <Stack hasGutter>
      <StackItem>
        <Alert variant="danger" isInline title="Calculation failed">
          {error.message}
        </Alert>
      </StackItem>
    </Stack>
  );
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4fe0d54 and 47f2f0f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • .cursor/plans/cluster_sizer_api_integration_4747313e.plan.md
  • .gitignore
  • package.json
  • specs/cluster-sizer.md
  • specs/cluster-switcher.md
  • src/pages/report/Report.tsx
  • src/pages/report/cluster-sizer/ClusterSizingWizard.tsx
  • src/pages/report/cluster-sizer/PopoverIcon.tsx
  • src/pages/report/cluster-sizer/SizingInputForm.tsx
  • src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/SizingResult.tsx
  • src/pages/report/cluster-sizer/SizingResultWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/constants.ts
  • src/pages/report/cluster-sizer/index.ts
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts
  • src/pages/report/cluster-sizer/mocks/data.mock.ts
  • src/pages/report/cluster-sizer/mocks/index.ts
  • src/pages/report/cluster-sizer/types.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-05T07:49:36.795Z
Learnt from: ammont82
Repo: kubev2v/migration-planner-ui-app PR: 248
File: src/pages/report/assessment-report/ClustersOverview.tsx:0-0
Timestamp: 2025-12-05T07:49:36.795Z
Learning: In src/pages/report/assessment-report/ClustersOverview.tsx, the CPU overcommitment view uses TOP_N=5 to show the top 5 clusters individually without aggregating remaining values, while vmByCluster and dataCenterDistribution views use TOP_N=4 and then add a "Rest of..." aggregated category, resulting in 5 total displayed items across all views but with different aggregation strategies.

Applied to files:

  • src/pages/report/cluster-sizer/SizingResult.tsx
  • src/pages/report/cluster-sizer/constants.ts
  • specs/cluster-sizer.md
  • src/pages/report/cluster-sizer/types.ts
  • src/pages/report/cluster-sizer/SizingInputForm.tsx
  • src/pages/report/cluster-sizer/ClusterSizingWizard.tsx
  • .cursor/plans/cluster_sizer_api_integration_4747313e.plan.md
  • src/pages/report/Report.tsx
🧬 Code graph analysis (5)
src/pages/report/cluster-sizer/PopoverIcon.tsx (1)
src/components/SVGIcon.tsx (1)
  • SVGIconProps (3-8)
src/pages/report/cluster-sizer/constants.ts (1)
src/pages/report/cluster-sizer/types.ts (4)
  • WorkerNodePreset (29-29)
  • OvercommitRatio (34-34)
  • HAReplicaCount (39-39)
  • SizingFormValues (44-57)
src/pages/report/cluster-sizer/mocks/data.mock.ts (2)
src/pages/report/cluster-sizer/types.ts (2)
  • SizingFormValues (44-57)
  • ClusterRequirementsResponse (18-18)
src/pages/report/cluster-sizer/constants.ts (1)
  • WORKER_NODE_PRESETS (11-33)
src/pages/report/cluster-sizer/SizingInputForm.tsx (3)
src/pages/report/cluster-sizer/types.ts (2)
  • SizingFormValues (44-57)
  • OvercommitRatio (34-34)
src/pages/report/cluster-sizer/index.ts (1)
  • SizingInputForm (2-2)
src/pages/report/cluster-sizer/constants.ts (3)
  • CPU_OPTIONS (38-44)
  • MEMORY_OPTIONS (49-55)
  • OVERCOMMIT_OPTIONS (60-91)
src/pages/report/Report.tsx (2)
src/pages/report/cluster-sizer/ClusterSizingWizard.tsx (1)
  • ClusterSizingWizard (31-148)
src/pages/report/cluster-sizer/index.ts (1)
  • ClusterSizingWizard (1-1)
🪛 GitHub Check: type-check
src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts

[failure] 11-11:
'"../types"' has no exported member named 'ResourceConsumption'. Did you mean 'SizingResourceConsumption'?

src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts

[failure] 9-9:
'"../types"' has no exported member named 'OvercommitRatioString'. Did you mean 'OvercommitRatio'?

🪛 markdownlint-cli2 (0.18.1)
specs/cluster-sizer.md

37-37: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


77-77: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: Red Hat Konflux / migration-planner-ui-on-pull-request
🔇 Additional comments (25)
src/pages/report/cluster-sizer/PopoverIcon.tsx (1)

19-48: LGTM!

The component implementation is clean:

  • Proper use of classNames for CSS composition
  • onClick={(e) => e.preventDefault()} correctly prevents form submission when used within forms
  • Props are well-destructured with sensible defaults
src/pages/report/cluster-sizer/mocks/index.ts (1)

1-8: LGTM!

Clean barrel file for mock exports with helpful JSDoc referencing the related PR.

src/pages/report/cluster-sizer/index.ts (1)

1-5: LGTM!

Well-structured barrel file establishing a clean public API surface for the cluster-sizer feature. Named exports for components and wildcard re-exports for types/constants is an appropriate pattern.

specs/cluster-sizer.md (1)

1-99: Good documentation coverage.

The spec comprehensively documents the feature including wizard flow, API contracts, UX behaviors, and integration points. This will serve as a valuable reference for the team.

package.json (1)

35-35: LGTM!

The dependency bump to ^0.0.37 aligns with the cluster sizer feature requirements for the new API types.

src/pages/report/Report.tsx (2)

43-43: LGTM: Clean integration of the ClusterSizingWizard.

The new imports and state management for the sizing wizard are well-structured and follow existing patterns in this file.

Also applies to: 59-59


344-354: LGTM: Good conditional visibility for the sizing wizard button.

The button correctly appears only when a specific cluster is selected (not "all"), and the placement alongside the download button provides logical grouping of cluster-related actions.

src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx (2)

23-26: Verify: Navigation proceeds even when calculation fails.

The handleNext function always calls goToNextStep() after onCalculate() completes, including when the API call fails (since the parent catches the error and doesn't re-throw). This means users navigate to the results step where the error is displayed.

If this is intentional UX (show error on results page), this is fine. Otherwise, consider conditionally navigating:

 const handleNext = async (): Promise<void> => {
-  await onCalculate();
-  goToNextStep();
+  try {
+    await onCalculate();
+    goToNextStep();
+  } catch {
+    // Stay on current step if calculation fails
+  }
 };

This would require the parent to re-throw the error after catching it.


28-52: LGTM: Clean wizard footer implementation.

The footer correctly disables both buttons during loading, preventing users from triggering multiple API calls or closing the wizard mid-request. The PatternFly component usage follows established patterns.

src/pages/report/cluster-sizer/SizingInputForm.tsx (2)

36-66: LGTM: Handler implementations are consistent and type-safe.

The handlers correctly use parseInt with radix 10, and the type assertion for OvercommitRatio is safe since the options are controlled by OVERCOMMIT_OPTIONS constants. Setting workerNodePreset to 'custom' on CPU/memory changes aligns with the form's intended behavior.


68-193: LGTM: Well-structured form with good accessibility.

The form follows PatternFly best practices with proper fieldId matching, aria-label attributes, and helpful tooltips via PopoverIcon. The dropdown-based inputs naturally prevent invalid input.

src/pages/report/cluster-sizer/SizingResultWizardStepFooter.tsx (1)

16-43: LGTM: Clean footer for the results step.

The footer correctly positions "Close" as the primary action for the final wizard step, with "Back" as a secondary option. Simple and effective implementation.

src/pages/report/cluster-sizer/SizingResult.tsx (2)

149-156: LGTM: Safe extraction of optional fields.

The nullish coalescing (??) correctly handles potentially undefined nested fields from the API response, providing sensible defaults of 0.


157-247: LGTM: Well-structured results panel.

The Panel component with sticky header and scrollable content provides good UX. The content is logically organized into cluster info, additional info, and resource breakdown sections with consistent formatting.

src/pages/report/cluster-sizer/ClusterSizingWizard.tsx (2)

31-95: LGTM! Clean DI-based API integration.

The component correctly uses dependency injection to obtain the AssessmentApi, properly manages loading/error states, and handles cleanup on close. The useCallback hooks have correct dependency arrays.


101-147: Well-structured wizard modal.

Good use of PatternFly's WizardHeader with close handler and custom step footers for controlling navigation flow. The aria-label on the Modal provides appropriate accessibility.

src/pages/report/cluster-sizer/mocks/data.mock.ts (1)

1-109: LGTM! Well-documented mock module.

The mock calculations align with the expected API behavior, providing reasonable test data. The TODO comment at the top clearly marks this as temporary code for development/testing.

src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts (1)

12-109: Good mock data coverage once type issue is resolved.

The factory function and predefined variants (small, large, min, max values) provide comprehensive test coverage. The structure is well-organized for unit testing scenarios.

.cursor/plans/cluster_sizer_api_integration_4747313e.plan.md (1)

1-110: Useful integration plan document.

The plan effectively documents the DI-based API integration approach, with clear implementation steps and code examples. Good reference for understanding the architectural decisions.

src/pages/report/cluster-sizer/constants.ts (1)

1-190: LGTM! Comprehensive and well-organized constants module.

The constants provide a clean, centralized source for form defaults, validation constraints, and UI labels. The type-safe definitions using OvercommitRatio, HAReplicaCount, and WorkerNodePreset ensure consistency with the type system.

src/pages/report/cluster-sizer/types.ts (3)

10-24: LGTM!

Clean import and re-export pattern that creates a single source of truth for API types within the cluster-sizer module.


26-62: LGTM!

Well-documented type definitions with clear JSDoc comments. The type constraints (literal unions for presets, ratios, and replica counts) provide good type safety.


86-100: No action required; haReplicas omission is intentional.

The haReplicas field is a frontend-only form value used for client-side calculations (determining the number of control plane nodes). The API payload does not require or accept this field—cluster control plane configuration is conveyed via the controlPlaneSchedulable boolean instead. All mock ClusterRequirementsRequest objects consistently exclude haReplicas, confirming this is by design.

Likely an incorrect or invalid review comment.

src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts (2)

17-42: LGTM!

Well-structured default mock with realistic values representing a typical medium-sized cluster.


195-247: Good factory pattern for flexible mock creation.

The factory functions with Partial<T> overrides provide a clean API for creating custom mocks in tests. The nullish coalescing defaults ensure complete objects are always returned.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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: 1

🤖 Fix all issues with AI agents
In `@src/pages/report/cluster-sizer/PopoverIcon.tsx`:
- Line 12: IconComponent is currently typed as
React.ComponentClass<SVGIconProps>, which excludes function components like
PatternFly icons; change the prop type to React.ComponentType<SVGIconProps> (or
React.FC<SVGIconProps>) so both class and function components are accepted,
update the IconComponent prop declaration in PopoverIcon.tsx accordingly, and
ensure any default assignment (e.g., OutlinedQuestionCircleIcon) and usages
still type-check after the change.
♻️ Duplicate comments (1)
src/pages/report/cluster-sizer/SizingResult.tsx (1)

72-73: Inconsistent bullet characters in plain text output.

Line 72 uses ~ while line 73 uses - for bullet points. This inconsistency also appears in the rendered UI (lines 220 and 223).

Proposed fix
-~ CPU Over-Commit Ratio: ${formatRatio(cpuOverCommitRatio)}
+- CPU Over-Commit Ratio: ${formatRatio(cpuOverCommitRatio)}
🧹 Nitpick comments (5)
src/pages/report/cluster-sizer/PopoverIcon.tsx (1)

9-17: Consider exporting PopoverIconProps for consumer type access.

If this component will be reused across the codebase, exporting the type definition would allow consumers to properly type their props when extending or wrapping this component.

Proposed change
-type PopoverIconProps = PopoverProps & {
+export type PopoverIconProps = PopoverProps & {

Also applies to: 50-50

specs/cluster-sizer.md (2)

37-39: Add language specifier to fenced code block.

The API endpoint code block is missing a language specifier. Per markdownlint, fenced code blocks should have a language specified.

Proposed fix
-```
+```text
 POST /api/v1/assessments/{id}/cluster-requirements
</details>

---

`77-86`: **Add language specifier to file structure code block.**

The file structure code block is missing a language specifier.

<details>
<summary>Proposed fix</summary>

```diff
-```
+```text
 src/pages/report/cluster-sizer/
 ├── index.ts                  # Re-exports
src/pages/report/cluster-sizer/SizingResult.tsx (1)

97-103: Consider providing user feedback on copy success/failure.

The copy handler silently logs errors to console. Consider adding user feedback (e.g., a toast notification or button state change) to indicate successful copy or failure.

src/pages/report/cluster-sizer/mocks/data.mock.ts (1)

53-56: Worker node calculation ignores memory requirements.

The node count is derived solely from CPU. With the mock inventory (281 CPU / 1117 GB), choosing a small preset (16 CPU / 64 GB) yields ~6 workers (with overcommit 1.0), providing 384 GB memory for 1117 GB needed—memory would be significantly oversubscribed.

This is acceptable for mock data, but ensure the real API considers both CPU and memory when determining minimum nodes.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47f2f0f and adb138a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • .cursor/plans/cluster_sizer_api_integration_4747313e.plan.md
  • .gitignore
  • package.json
  • specs/cluster-sizer.md
  • specs/cluster-switcher.md
  • src/pages/report/Report.tsx
  • src/pages/report/cluster-sizer/ClusterSizingWizard.tsx
  • src/pages/report/cluster-sizer/PopoverIcon.tsx
  • src/pages/report/cluster-sizer/SizingInputForm.tsx
  • src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/SizingResult.tsx
  • src/pages/report/cluster-sizer/SizingResultWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/constants.ts
  • src/pages/report/cluster-sizer/index.ts
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts
  • src/pages/report/cluster-sizer/mocks/data.mock.ts
  • src/pages/report/cluster-sizer/mocks/index.ts
  • src/pages/report/cluster-sizer/types.ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • .cursor/plans/cluster_sizer_api_integration_4747313e.plan.md
  • src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/SizingResultWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/SizingInputForm.tsx
  • src/pages/report/cluster-sizer/constants.ts
  • package.json
  • src/pages/report/cluster-sizer/ClusterSizingWizard.tsx
  • src/pages/report/cluster-sizer/mocks/index.ts
  • .gitignore
  • src/pages/report/Report.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-05T07:49:36.795Z
Learnt from: ammont82
Repo: kubev2v/migration-planner-ui-app PR: 248
File: src/pages/report/assessment-report/ClustersOverview.tsx:0-0
Timestamp: 2025-12-05T07:49:36.795Z
Learning: In src/pages/report/assessment-report/ClustersOverview.tsx, the CPU overcommitment view uses TOP_N=5 to show the top 5 clusters individually without aggregating remaining values, while vmByCluster and dataCenterDistribution views use TOP_N=4 and then add a "Rest of..." aggregated category, resulting in 5 total displayed items across all views but with different aggregation strategies.

Applied to files:

  • specs/cluster-sizer.md
  • src/pages/report/cluster-sizer/types.ts
  • src/pages/report/cluster-sizer/SizingResult.tsx
🧬 Code graph analysis (4)
src/pages/report/cluster-sizer/mocks/data.mock.ts (2)
src/pages/report/cluster-sizer/types.ts (2)
  • SizingFormValues (44-57)
  • ClusterRequirementsResponse (18-18)
src/pages/report/cluster-sizer/constants.ts (1)
  • WORKER_NODE_PRESETS (11-33)
src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts (1)
src/pages/report/cluster-sizer/types.ts (1)
  • ClusterRequirementsRequest (17-17)
src/pages/report/cluster-sizer/SizingResult.tsx (4)
src/pages/report/cluster-sizer/types.ts (1)
  • SizingFormValues (44-57)
src/utils/formatters.ts (1)
  • formatNumber (8-16)
src/pages/report/cluster-sizer/constants.ts (1)
  • OVERCOMMIT_OPTIONS (60-91)
src/pages/report/cluster-sizer/index.ts (1)
  • SizingResult (3-3)
src/pages/report/cluster-sizer/PopoverIcon.tsx (1)
src/components/SVGIcon.tsx (1)
  • SVGIconProps (3-8)
🪛 markdownlint-cli2 (0.18.1)
specs/cluster-sizer.md

37-37: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


77-77: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (17)
src/pages/report/cluster-sizer/PopoverIcon.tsx (1)

19-48: LGTM!

The component implementation is clean and well-structured. The default values are sensible, the onClick handler correctly prevents default behavior (useful when inside forms), and the prop spreading to Popover is appropriate.

src/pages/report/cluster-sizer/types.ts (3)

1-24: LGTM! Clean type organization and re-exports.

The type definitions are well-documented, and the re-export pattern from the API client keeps the import surface clean for consumers.


67-84: LGTM! Exhaustive enum mapping.

The OVERCOMMIT_RATIO_MAP correctly maps all OvercommitRatio values to their API enum counterparts, ensuring type safety at compile time.


89-100: haReplicas field is not included in the API request payload.

The SizingFormValues interface includes haReplicas (line 52), but formValuesToRequest does not map it to the API payload. If this field is intended for future use, consider adding a TODO comment. Otherwise, verify if it should be included in ClusterRequirementsRequest.

src/pages/report/cluster-sizer/SizingResult.tsx (3)

30-38: LGTM! Local formatting functions are appropriate.

The local formatNumber using toLocaleString() is intentionally different from the shared utility in src/utils/formatters.ts (which uses K/M abbreviations). Locale-specific formatting is more suitable for the detailed recommendation display.


105-147: LGTM! Proper handling of loading, error, and empty states.

The component correctly handles all three edge cases with appropriate UI feedback before rendering the main content.


149-156: LGTM! Safe optional field extraction with sensible defaults.

The nullish coalescing pattern for extracting nested optional fields (overCommitRatio, limits) ensures the component won't crash if the API response has missing fields.

src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts (3)

7-18: LGTM! Well-structured mock data with proper type import.

The import from '../types' correctly uses the re-exported ClusterRequirementsRequest type, and the mock provides realistic default values.


79-93: LGTM! Factory function provides good flexibility for testing.

The createMockClusterRequirementsRequest factory with partial overrides enables easy creation of custom test scenarios while maintaining type safety.


98-106: LGTM! Convenient lookup map for over-commit ratio variants.

The mockRequestsByOvercommitRatio record provides type-safe access to pre-built request mocks for each over-commit ratio, useful for parameterized tests.

src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts (3)

7-12: LGTM! Import issue resolved.

The import now correctly uses SizingResourceConsumption as re-exported from '../types'.


17-42: LGTM! Comprehensive default mock response.

The mockClusterRequirementsResponse provides a realistic medium-sized cluster scenario with all nested fields populated, serving as a good baseline for tests.


198-247: LGTM! Composable factory functions.

The factory functions for nested types (createMockClusterSizing, createMockInventoryTotals, createMockResourceConsumption) enable flexible test data composition while createMockClusterRequirementsResponse ties them together with sensible defaults.

src/pages/report/cluster-sizer/mocks/data.mock.ts (3)

1-23: LGTM!

Clear documentation with ticket reference for future cleanup. The as const assertion on MOCK_INVENTORY ensures type safety and immutability.


84-91: Verify the semantics of limits and overCommitRatio fields.

The limits values are computed as inventory * overcommitRatio, which represents the inventory's resource needs adjusted by overcommit. Typically, "limits" in cluster sizing refers to maximum schedulable resources (clusterCapacity * overcommitRatio).

Similarly, overCommitRatio here computes actual utilization ratios (inventory / totalCluster), not the user-configured overcommit ratio.

Please verify these calculations align with the API specification in ECOPROJECT-3631.


96-109: LGTM!

Clean async wrapper with configurable delay for realistic API simulation during development.

src/pages/report/cluster-sizer/index.ts (1)

1-5: LGTM!

Clean barrel file providing a single entry point for the cluster-sizer module. Good practice to exclude mocks from the public exports—they remain accessible via direct import when needed for testing.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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: 2

🤖 Fix all issues with AI agents
In `@src/pages/report/cluster-sizer/mocks/index.ts`:
- Around line 1-8: The file header comment incorrectly references PR `#819` and
ECOPROJECT-3719; update the top comment block above the exports in
src/pages/report/cluster-sizer/mocks/index.ts to reference the correct PR number
and ticket (PR `#308` / ECOPROJECT-3706) or explicitly state that PR `#819` is a
related backend change — edit the JSDoc comment string at the top of this module
to reflect the accurate PR/ticket or clarify the relationship.

In `@src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx`:
- Around line 23-26: The handleNext function currently awaits onCalculate()
without error handling, causing unhandled promise rejections and no user
feedback; wrap the await in a try-catch inside handleNext, only call
goToNextStep() on success, and in the catch either call a provided error
callback (e.g., an onError prop) or set a local error state so the component can
display the error and avoid unhandled rejections while preserving correct
non-advancement on failure.
♻️ Duplicate comments (1)
src/pages/report/cluster-sizer/SizingResult.tsx (1)

72-73: Inconsistent bullet characters.

Lines 72 and 73 use different bullet characters (~ vs -). This inconsistency also appears in the UI render (lines 220, 223).

🧹 Nitpick comments (6)
src/pages/report/cluster-sizer/constants.ts (1)

35-55: Minor discrepancy with spec documentation.

The spec (specs/cluster-sizer.md line 20) lists CPU options as (8, 16, 32, 64, 96, 128), but CPU_OPTIONS here defines [16, 32, 64, 128, 200]. Similarly, memory options differ. Please ensure the spec is updated to match the implementation, or clarify if this is an intentional deviation from the Figma design.

specs/cluster-sizer.md (2)

37-39: Add language specifier to fenced code block.

Per linting rules (MD040), fenced code blocks should have a language specified for syntax highlighting and accessibility.

📝 Suggested fix
-```
+```text
 POST /api/v1/assessments/{id}/cluster-requirements
</details>

---

`76-86`: **Update file structure to match implementation.**

The documented structure shows `mockData.ts`, but the actual implementation uses a `mocks/` directory with multiple files. Also, add a language specifier to the code block.


<details>
<summary>📝 Suggested fix</summary>

```diff
-```
+```text
 src/pages/report/cluster-sizer/
 ├── index.ts                  # Re-exports
 ├── types.ts                  # TypeScript interfaces and type definitions
 ├── constants.ts              # Form options (CPU, memory, over-commit dropdowns)
 ├── ClusterSizingWizard.tsx   # Main wizard modal component
 ├── SizingInputForm.tsx       # Step 1: Migration preferences form
 ├── SizingResult.tsx          # Step 2: Results display
-└── mockData.ts               # Mock API responses for development
+└── mocks/                    # Mock API responses for development
+    ├── index.ts
+    ├── clusterRequirementsRequest.mock.ts
+    ├── clusterRequirementsResponse.mock.ts
+    └── data.mock.ts
src/pages/report/cluster-sizer/SizingInputForm.tsx (1)

36-66: Consider adding Number() instead of parseInt for consistency.

Since the option values are already numbers converted to strings, Number(cpu) would be cleaner than parseInt(cpu, 10). However, this is a minor stylistic preference and the current implementation is correct.

♻️ Optional refactor
   const handleCpuChange = (
     _event: React.FormEvent<HTMLSelectElement>,
     cpu: string,
   ): void => {
     onChange({
       ...values,
       workerNodePreset: 'custom',
-      customCpu: parseInt(cpu, 10),
+      customCpu: Number(cpu),
     });
   };
src/pages/report/cluster-sizer/SizingResult.tsx (2)

30-38: Consider clarifying the local formatNumber purpose.

There's a formatNumber utility in src/utils/formatters.ts that produces compact output (1.2M), while this local version produces locale-formatted output (1,234,567). Both are valid but the naming overlap could cause confusion. Consider either:

  • Renaming to formatNumberLocale for clarity, or
  • Adding a brief comment distinguishing from the utility version

97-103: Consider adding user feedback for copy action.

The clipboard copy silently succeeds or fails with only a console log on error. Users would benefit from visual feedback (e.g., a toast notification or temporary button text change like "Copied!") to confirm the action succeeded.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adb138a and a160afc.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • .cursor/plans/cluster_sizer_api_integration_4747313e.plan.md
  • .gitignore
  • package.json
  • specs/cluster-sizer.md
  • specs/cluster-switcher.md
  • src/pages/report/Report.tsx
  • src/pages/report/cluster-sizer/ClusterSizingWizard.tsx
  • src/pages/report/cluster-sizer/PopoverIcon.tsx
  • src/pages/report/cluster-sizer/SizingInputForm.tsx
  • src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/SizingResult.tsx
  • src/pages/report/cluster-sizer/SizingResultWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/constants.ts
  • src/pages/report/cluster-sizer/index.ts
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts
  • src/pages/report/cluster-sizer/mocks/data.mock.ts
  • src/pages/report/cluster-sizer/mocks/index.ts
  • src/pages/report/cluster-sizer/types.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • package.json
  • src/pages/report/cluster-sizer/index.ts
  • src/pages/report/cluster-sizer/SizingResultWizardStepFooter.tsx
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsRequest.mock.ts
  • src/pages/report/cluster-sizer/PopoverIcon.tsx
  • src/pages/report/Report.tsx
  • src/pages/report/cluster-sizer/ClusterSizingWizard.tsx
  • src/pages/report/cluster-sizer/mocks/data.mock.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ammont82
Repo: kubev2v/migration-planner-ui-app PR: 248
File: src/pages/report/assessment-report/ClustersOverview.tsx:0-0
Timestamp: 2025-12-05T07:49:36.795Z
Learning: In src/pages/report/assessment-report/ClustersOverview.tsx, the CPU overcommitment view uses TOP_N=5 to show the top 5 clusters individually without aggregating remaining values, while vmByCluster and dataCenterDistribution views use TOP_N=4 and then add a "Rest of..." aggregated category, resulting in 5 total displayed items across all views but with different aggregation strategies.
📚 Learning: 2025-12-05T07:49:36.795Z
Learnt from: ammont82
Repo: kubev2v/migration-planner-ui-app PR: 248
File: src/pages/report/assessment-report/ClustersOverview.tsx:0-0
Timestamp: 2025-12-05T07:49:36.795Z
Learning: In src/pages/report/assessment-report/ClustersOverview.tsx, the CPU overcommitment view uses TOP_N=5 to show the top 5 clusters individually without aggregating remaining values, while vmByCluster and dataCenterDistribution views use TOP_N=4 and then add a "Rest of..." aggregated category, resulting in 5 total displayed items across all views but with different aggregation strategies.

Applied to files:

  • src/pages/report/cluster-sizer/SizingInputForm.tsx
  • .cursor/plans/cluster_sizer_api_integration_4747313e.plan.md
  • src/pages/report/cluster-sizer/constants.ts
  • src/pages/report/cluster-sizer/SizingResult.tsx
  • src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts
  • specs/cluster-sizer.md
🧬 Code graph analysis (4)
src/pages/report/cluster-sizer/SizingInputForm.tsx (3)
src/pages/report/cluster-sizer/types.ts (2)
  • SizingFormValues (44-57)
  • OvercommitRatio (34-34)
src/pages/report/cluster-sizer/index.ts (1)
  • SizingInputForm (2-2)
src/pages/report/cluster-sizer/constants.ts (3)
  • CPU_OPTIONS (38-44)
  • MEMORY_OPTIONS (49-55)
  • OVERCOMMIT_OPTIONS (60-91)
src/pages/report/cluster-sizer/constants.ts (1)
src/pages/report/cluster-sizer/types.ts (4)
  • WorkerNodePreset (29-29)
  • OvercommitRatio (34-34)
  • HAReplicaCount (39-39)
  • SizingFormValues (44-57)
src/pages/report/cluster-sizer/SizingResult.tsx (4)
src/pages/report/cluster-sizer/types.ts (2)
  • SizingFormValues (44-57)
  • ClusterRequirementsResponse (18-18)
src/utils/formatters.ts (1)
  • formatNumber (8-16)
src/pages/report/cluster-sizer/constants.ts (1)
  • OVERCOMMIT_OPTIONS (60-91)
src/pages/report/cluster-sizer/index.ts (1)
  • SizingResult (3-3)
src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts (1)
src/pages/report/cluster-sizer/types.ts (4)
  • ClusterRequirementsResponse (18-18)
  • ClusterSizing (19-19)
  • InventoryTotals (20-20)
  • SizingResourceConsumption (22-22)
🪛 markdownlint-cli2 (0.18.1)
specs/cluster-sizer.md

37-37: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


77-77: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (18)
.gitignore (1)

5-7: LGTM!

The .gitignore pattern is now correct. Using .cursor/* allows Git to traverse into the directory, and the negation patterns properly include .cursor/plans/ and its contents.

src/pages/report/cluster-sizer/types.ts (3)

1-24: LGTM!

The module organization is clean with clear separation between API type re-exports and UI-specific types. JSDoc comments provide helpful context linking to the Jira ticket.


26-62: LGTM!

The UI types are well-designed with appropriate use of literal types for constrained values and comprehensive JSDoc documentation for each field.


86-100: Verify: haReplicas is not included in the API request.

The formValuesToRequest function excludes haReplicas from the ClusterRequirementsRequest payload. Based on the spec, this appears intentional since the API schema doesn't include it. Please confirm that HA configuration is handled separately or is purely UI state for display purposes.

src/pages/report/cluster-sizer/mocks/clusterRequirementsResponse.mock.ts (3)

1-42: LGTM!

The mock module is well-organized with clear documentation. The default mock response provides representative values for a medium-sized cluster scenario.


44-193: LGTM!

Good coverage of different cluster scenarios with realistic data. The mocks appropriately vary resource consumption, overcommit ratios, and cluster sizes to support comprehensive testing.


195-247: LGTM!

The factory functions are well-designed with sensible defaults and proper composition. Using nullish coalescing (??) correctly preserves explicit 0 or false overrides.

src/pages/report/cluster-sizer/constants.ts (4)

1-33: LGTM!

The worker node presets are well-defined with clear labels and descriptions. The type exclusion for 'custom' is appropriate since custom doesn't have predefined CPU/memory values.


57-120: LGTM!

The overcommit and HA options are comprehensive with clear user-facing labels, descriptions, and help text. The values correctly match their respective type constraints.


138-148: Verify: Default overcommit ratio differs from spec.

The spec (specs/cluster-sizer.md line 90) states the default over-commit is 1:6, but DEFAULT_FORM_VALUES.overcommitRatio is set to 4 (1:4). Please confirm which is correct - 1:4 is more conservative and may be a safer default, but the spec should be aligned with the implementation.


150-190: LGTM!

Validation constraints are reasonable, and form labels provide helpful context for users making sizing decisions.

specs/cluster-sizer.md (1)

88-96: Default over-commit value is inconsistent with implementation.

Line 90 states the default over-commit is 1:6, but constants.ts sets overcommitRatio: 4 (which maps to 1:4). Please align the spec with the actual implementation.

src/pages/report/cluster-sizer/SizingInputForm.tsx (2)

1-28: LGTM! Clean controlled form component.

The component follows good patterns with immutable state updates and clean separation of concerns. The props interface with values and onChange creates a proper controlled component pattern.


68-198: Well-structured form with good accessibility.

The form properly uses PatternFly components, includes aria-labels for accessibility, and provides helpful popovers for user guidance. The Grid/Stack layout is appropriate for the form structure.

src/pages/report/cluster-sizer/SizingInputFormWizardStepFooter.tsx (1)

28-53: LGTM! Proper wizard footer implementation.

The footer correctly uses PatternFly's wizard patterns with appropriate loading states and button disabling during async operations.

src/pages/report/cluster-sizer/SizingResult.tsx (2)

149-155: Good defensive handling of optional API fields.

The null coalescing for overCommitRatio and limits aligns with the API integration plan noting these fields are optional in the api-client types.


81-147: Well-structured state handling.

The component properly handles loading, error, and empty data states with appropriate UI feedback for each case. The memoization of plainTextRecommendation is a good optimization.

.cursor/plans/cluster_sizer_api_integration_4747313e.plan.md (1)

1-110: No action needed. This plan file is intentionally committed to the repository.

The .gitignore explicitly whitelists .cursor/plans/ with !.cursor/plans/ and !.cursor/plans/**, indicating the team deliberately includes these plan files in version control for shared documentation purposes. This is not an oversight.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

… cluster recommendations

- Added a new `ClusterSizingWizard` component to guide users through selecting migration preferences and reviewing cluster sizing recommendations.
- Implemented a two-step wizard that collects user inputs for worker node configurations and displays calculated requirements based on VMware inventory data.
- Created supporting components including `SizingInputForm`, `SizingResult`, and their respective footers for navigation.
- Integrated mock data for development and testing purposes, with a plan for future API integration.

This feature enhances the user experience by providing tailored cluster sizing recommendations, facilitating smoother migrations to OpenShift.

Signed-off-by: Jonathan Kilzi <jkilzi@redhat.com>
ECOPROJECT-3706 | feat: Update Cluster Sizer: Plan, API integration and dependencies

- Updated the `.gitignore` to include specific cursor plans while excluding others.
- Bumped the version of `@migration-planner-ui/api-client` from `0.0.36` to `0.0.37` in both `package.json` and `package-lock.json`.
- Introduced a new plan document for the Cluster Sizer API integration detailing implementation steps and key files.
- Refactored `ClusterSizingWizard` to utilize the new API client for fetching cluster requirements, removing mock data dependencies.
- Enhanced `SizingResult` and `SizingInputForm` components to handle optional API response fields and improve user feedback.
- Cleaned up code formatting and comments for better readability.

This update improves the integration of the Cluster Sizer feature with the backend API, enhancing the overall user experience in the migration planning process.

Signed-off-by: Jonathan Kilzi <jkilzi@redhat.com>
@openshift-ci
Copy link

openshift-ci bot commented Jan 15, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ammont82

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

@ammont82
Copy link
Collaborator

/retest

@jkilzi jkilzi merged commit f567f9d into kubev2v:master Jan 15, 2026
12 checks passed
@jkilzi jkilzi deleted the ECOPROJECT-3706 branch January 15, 2026 06:59
@jkilzi jkilzi restored the ECOPROJECT-3706 branch January 15, 2026 12:38
@jkilzi jkilzi deleted the ECOPROJECT-3706 branch January 15, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants