Repository navigation
[Customer Portal][FE][Web] Refactor Updates Module: Transition to Recommended Update Levels API and Cleanup Legacy Stats - #164
Conversation
Introduce a new Vitest test suite for the useGetRecommendedUpdateLevels hook. The tests mock logger, Asgardeo auth, mock config provider and API delay, and exercise cases for initial loading, mock-data flow, query options (staleTime), real API success and error (by stubbing fetch and window.config), and the empty projectId no-fetch behavior. Ensures proper logging calls and token retrieval are asserted.
Delete apps/customer-portal/webapp/src/api/__tests__/useGetUpdatesStats.test.tsx. The removed file contained unit tests for the useGetUpdatesStats hook, including mocks for Asgardeo, fetch, and mock config, and covered mock vs API responses, error handling, query options, and empty projectId behavior. This change cleans up or relocates the obsolete/removed tests.
Delete apps/customer-portal/webapp/src/api/useGetProductUpdatesStats.ts which implemented a React Query hook for fetching product updates statistics (handled mock mode and authenticated API calls). Remove this unused/obsolete hook and ensure any references or imports are updated accordingly.
Introduce a new React Query hook to fetch recommended update levels for a project. The hook (apps/customer-portal/webapp/src/api/useGetRecommendedUpdateLevels.ts) supports mock mode with configurable delay, uses the authenticated API client from AuthApiContext, and logs progress via useLogger. It validates CUSTOMER_PORTAL_BACKEND_BASE_URL, handles HTTP errors, and enables the query only when a projectId is present and authentication (or mock) conditions are met. Query results are cached with a 5-minute staleTime.
Delete the custom hook at apps/customer-portal/webapp/src/api/useGetUpdatesStats.ts. The hook previously fetched updates statistics using react-query, handled auth (useAsgardeo/useAuthApiClient), mock responses, and logging. Remove any imports/usages of this hook elsewhere in the codebase.
Add a data-testid attribute (error-indicator) to the IconButton in the ErrorIndicator component to make it easier to target the element in unit and integration tests.
Delete the unit test file for the UpdatesStatsGrid component (apps/customer-portal/webapp/src/components/updates/__tests__/UpdatesStatsGrid.test.tsx). This removes tests that covered headings, stat labels/values, null placeholders, and loading state skeletons. Replace or update tests if this deletion was not intended.
Introduce unit tests for the StatCard component (apps/.../StatCard.test.tsx). Tests cover rendering of label and value, loading state (skeleton hides value), error state (renders error-indicator), and rendering of extraContent. Uses Vitest and React Testing Library and includes license header.
Introduce a new StatCard component at apps/customer-portal/webapp/src/components/updates/stat-card-row/StatCard.tsx. The component renders a statistic card with an icon, colored background, value (with loading Skeleton and ErrorIndicator states), label, tooltip, and optional extra content. It uses the app theme and StatCardColor for styling and exports the typed StatCardProps interface.
Replace useGetProductUpdatesStats with useGetRecommendedUpdateLevels and update the UpdatesStatsGrid import path. Update the error banner and logger messages to reference "recommended update levels" instead of "updates statistics" to align the page with the new hook/API.
Add a new RecommendedUpdateLevelItem interface to models/responses.ts to represent a single product's recommended update level and related metadata (productName, productBaseVersion, channel, starting/ending/recommended levels, installed/available update counts, and timestamp). This provides a typed shape for frontend usage of recommended update data.
Import RecommendedUpdateLevelItem and add mockRecommendedUpdateLevels to support the /updates/recommended-update-levels endpoint. Adjust mockUpdatesStats counts/breakdowns (totalInstalled and pending/security numbers) and make minor formatting changes to project/product/deployment objects and a vulnerability resolution string for consistency.
Update ApiQueryKeys in apps/customer-portal/webapp/src/constants/apiConstants.ts: replace the UPDATES_STATS key value "updates-stats" with "recommended-update-levels" to reflect the new naming/semantics. Verify and update any usages that reference the old key.
Delete the UpdatesStatsGrid.tsx component which rendered the "Overall Update Status" grid of StatCard items (used UPDATES_STATS, StatCard, Grid, Typography). Removes apps/customer-portal/webapp/src/components/updates/UpdatesStatsGrid.tsx. Likely part of a refactor or component consolidation; no other context available in this changeset.
Introduce UpdatesStatsGrid.tsx: a new component that aggregates RecommendedUpdateLevelItem data and renders a grid of StatCard entries for Overall Update Status. Computes totals and breakdowns (installed/pending, regular/security), handles loading and error states with skeletons, and shows an action badge when security updates are pending. Exports UpdatesStatsGrid and its props interface; relies on UPDATES_STATS, StatCard, and oxygen-ui primitives.
Add a new test suite for UpdatesStatsGrid (apps/customer-portal/.../UpdatesStatsGrid.test.tsx). The tests mock the StatCard to avoid deep rendering and verify aggregated values from mockRecommendedUpdateLevels, plus loading (skeleton) and error placeholder behaviors. Uses Vitest and React Testing Library.
Introduce getMockRecommendedUpdateLevels() which returns mockRecommendedUpdateLevels (RecommendedUpdateLevelItem[]). Update imports to include mockRecommendedUpdateLevels and RecommendedUpdateLevelItem, add JSDoc for the new helper, and reformat getMockProductUpdateLevels for consistency.
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughRefactors the updates statistics data flow by renaming the hook from Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
Refactors the Customer Portal Updates module to stop using legacy “updates stats” APIs and instead fetch “recommended update levels”, while updating the UI layer to aggregate and present equivalent summary stats from the new response shape.
Changes:
- Replaced the legacy updates stats hook usage with
useGetRecommendedUpdateLevels, including new query key, endpoint, response typing, and updated hook tests. - Reworked the Updates “Overall Update Status” UI to compute aggregate stats from recommended update level items (new
StatCard+ newUpdatesStatsGridlocation), and removed the old grid implementation/tests. - Extended mocks/types to support the new API response shape and improved ErrorIndicator test targeting.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/customer-portal/webapp/src/pages/UpdatesPage.tsx | Switches the Updates page to the new hook and grid location; updates error messaging/logging. |
| apps/customer-portal/webapp/src/models/responses.ts | Adds RecommendedUpdateLevelItem response type for the new API. |
| apps/customer-portal/webapp/src/models/mockFunctions.ts | Adds mock accessor for recommended update levels; legacy mock stats accessor remains. |
| apps/customer-portal/webapp/src/models/mockData.ts | Adds mockRecommendedUpdateLevels; keeps legacy mockUpdatesStats fixture. |
| apps/customer-portal/webapp/src/constants/apiConstants.ts | Replaces UPDATES_STATS query key with RECOMMENDED_UPDATE_LEVELS. |
| apps/customer-portal/webapp/src/components/updates/stat-card-row/tests/UpdatesStatsGrid.test.tsx | New tests for the updated grid that aggregates from recommended update level items. |
| apps/customer-portal/webapp/src/components/updates/stat-card-row/tests/StatCard.test.tsx | New unit tests for the new StatCard component (loading/error/extra content). |
| apps/customer-portal/webapp/src/components/updates/stat-card-row/UpdatesStatsGrid.tsx | New grid that aggregates stats from recommended update levels and renders stat cards. |
| apps/customer-portal/webapp/src/components/updates/stat-card-row/StatCard.tsx | New stat card component for the Updates page. |
| apps/customer-portal/webapp/src/components/updates/tests/UpdatesStatsGrid.test.tsx | Removes legacy grid tests. |
| apps/customer-portal/webapp/src/components/updates/UpdatesStatsGrid.tsx | Removes legacy grid implementation. |
| apps/customer-portal/webapp/src/components/common/error-indicator/ErrorIndicator.tsx | Adds data-testid="error-indicator" to the icon button to support tests. |
| apps/customer-portal/webapp/src/api/useGetRecommendedUpdateLevels.ts | Implements the new recommended update levels hook (mock + API fetch). |
| apps/customer-portal/webapp/src/api/useGetProductUpdatesStats.ts | Removes the legacy product updates stats hook. |
| apps/customer-portal/webapp/src/api/tests/useGetRecommendedUpdateLevels.test.tsx | Updates hook tests for the new hook name/response shape/query key. |
Comments suppressed due to low confidence (3)
apps/customer-portal/webapp/src/api/useGetRecommendedUpdateLevels.ts:84
- The hook throws an error message that still says "updates stats" when the request fails. This is inconsistent with the new hook purpose (recommended update levels) and causes the updated test expectation to fail; update the thrown error message to reference recommended update levels (and ideally match the hook name).
apps/customer-portal/webapp/src/api/tests/useGetRecommendedUpdateLevels.test.tsx:61 - There is still a legacy test file (
src/api/__tests__/useGetProductUpdatesStats.test.tsx) importing@api/useGetProductUpdatesStats, but the hook implementation file is removed in this PR. That will break the test suite/module resolution; remove or update the legacy test to target the newuseGetRecommendedUpdateLevelshook.
apps/customer-portal/webapp/src/models/mockFunctions.ts:346 getMockUpdatesStatsnow appears to be unused (no hooks reference it after the refactor). Consider removing this function (and the correspondingmockUpdatesStatsimport) to complete the legacy stats cleanup and avoid carrying dead mock data.
/**
* Returns mock updates statistics.
*
* @returns {UpdatesStats} Mock updates statistics.
*/
export const getMockUpdatesStats = (): UpdatesStats => mockUpdatesStats;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/customer-portal/webapp/src/pages/UpdatesPage.tsx (1)
45-49:⚠️ Potential issue | 🟡 MinorPotential infinite loader when
projectIdis missing.If
projectIdisundefined(e.g., route mismatch), the hook receives""soenabledbecomesfalse. React Query won't fire the query, leavingdataasundefinedandisErrorasfalse— makingisUpdatesLoadingpermanentlytrueand the loader never hides.Consider guarding early or adjusting the derived loading state:
Option: short-circuit when projectId is absent
+ if (!projectId) { + return <Typography color="error">Project not found.</Typography>; + } + const { data, isLoading, isError } = useGetRecommendedUpdateLevels( - projectId || "", + projectId, );apps/customer-portal/webapp/src/api/useGetRecommendedUpdateLevels.ts (1)
80-83:⚠️ Potential issue | 🔴 CriticalStale error message: still references "updates stats" instead of "recommended update levels".
This is the root cause of the test assertion mismatch flagged in
useGetRecommendedUpdateLevels.test.tsx(line 212). The error text was not updated during the rename.Proposed fix
if (!response.ok) { throw new Error( - `Error fetching updates stats: ${response.statusText}`, + `Error fetching recommended update levels: ${response.statusText}`, ); }
🤖 Fix all issues with AI agents
In
`@apps/customer-portal/webapp/src/api/__tests__/useGetRecommendedUpdateLevels.test.tsx`:
- Around line 210-213: The test failure is caused by a mismatch between the
hook's thrown error text and the test expectation; update the error message
thrown in the hook useGetRecommendedUpdateLevels (the throw at the response
handling around the current `throw new Error(\`Error fetching updates stats:
${response.statusText}\`)` site) to match the test expectation by changing it to
`Error fetching recommended update levels: ${response.statusText}`, or
alternatively update the test assertion to expect the existing message `Error
fetching updates stats: Internal Server Error`; pick one approach and make the
message in either useGetRecommendedUpdateLevels or the test consistent.
In `@apps/customer-portal/webapp/src/api/useGetRecommendedUpdateLevels.ts`:
- Around line 32-34: The function useGetRecommendedUpdateLevels currently
accepts a projectId and uses it in the query key but does not include it in the
fetch request URL; either drop the projectId parameter from
useGetRecommendedUpdateLevels (and remove it from the query key) if the backend
endpoint is project-agnostic, or make the request project-scoped by adding
projectId to the request URL (same style as useGetProjectSupportStats) and keep
it in the query key; locate the logic inside useGetRecommendedUpdateLevels
(query key creation and the fetch call) and update the signature and query key
or the fetch URL accordingly so the parameter usage is consistent.
In
`@apps/customer-portal/webapp/src/components/updates/stat-card-row/StatCard.tsx`:
- Around line 32-41: The StatCardProps type declares value as required but the
component uses value ?? "N/A", so make value optional to allow the fallback;
change the prop signature in StatCardProps to value?: string | number, ensure
any usages inside the StatCard component (and callers) handle undefined
appropriately (the existing value ?? "N/A" expression is fine), and update any
tests or call sites that relied on value being required to pass a value or omit
it intentionally.
🧹 Nitpick comments (4)
apps/customer-portal/webapp/src/components/updates/stat-card-row/StatCard.tsx (1)
84-88: Redundantas StatCardColorcasts.
iconColoris already typed asStatCardColor, so the casts on lines 85, 88, and 103 are unnecessary noise.Proposed fix (example for lines 84-88)
- bgcolor: alpha( - theme.palette[iconColor as StatCardColor].light, - 0.1, - ), - color: theme.palette[iconColor as StatCardColor].light, + bgcolor: alpha( + theme.palette[iconColor].light, + 0.1, + ), + color: theme.palette[iconColor].light,apps/customer-portal/webapp/src/components/updates/stat-card-row/__tests__/UpdatesStatsGrid.test.tsx (1)
59-72: Loading and error tests are identical and don't distinguish the two states.Both
"shows skeletons when loading"and"renders placeholders when there is an error"assertgetAllByText("--")).toHaveLength(4). Since the mockedStatCardrenders{value}directly (ignoringisLoading/isError), these tests only verify that the grid passes"--"as the value whendataisundefined— they don't confirm thatisLoadingorisErroris propagated toStatCard.Consider extending the mock to capture and assert on the
isLoading/isErrorprops:Sketch
vi.mock("@components/updates/stat-card-row/StatCard", () => ({ - StatCard: ({ label, value }: { label: string; value: string | number }) => ( + StatCard: ({ label, value, isLoading, isError }: { label: string; value: string | number; isLoading?: boolean; isError?: boolean }) => ( <div data-testid={`stat-card-${label}`}> <span data-testid="card-label">{label}</span> <span data-testid="card-value">{value}</span> + {isLoading && <span data-testid="loading" />} + {isError && <span data-testid="error" />} </div> ), }));apps/customer-portal/webapp/src/components/updates/stat-card-row/UpdatesStatsGrid.tsx (2)
208-208: MoveisEffectiveLoadingabove its first usage.
isEffectiveLoadingis declared on line 208 but referenced insiderenderCountWithSkeleton(line 119) andrenderExtraContent(lines 141, 157, 174). While this works at runtime because the closures are only invoked in the JSX return block (after line 208), declaring the variable after its referencing closures is confusing and fragile — a future refactor could easily invoke one of these helpers earlier and hit a TDZ error.Proposed fix
Move the declaration to right after
aggregatedData:+ const isEffectiveLoading = isLoading || (!data && !isError); + const getValue = (id: keyof UpdatesStats): string | number => {And remove the duplicate at line 208:
- const isEffectiveLoading = isLoading || (!data && !isError); - return (
132-206: Duplicated rendering logic for "installed" and "pending" breakdown sections.The blocks on lines 139–153 and 155–169 are nearly identical, differing only in the
stat.idcheck and the data source (totalUpdatesInstalledBreakdownvstotalUpdatesPendingBreakdown). Consider extracting a shared helper to reduce duplication.
Introduce updates utilities and unit tests: add utils/updates.ts with aggregateUpdateStats, getStatValue, getStatTooltipText and NULL_PLACEHOLDER to compute and format update summary statistics; add Vitest tests (utils/__tests__/updates.test.ts) covering aggregation, value retrieval and tooltip text. Also tweak UpdatesPage to call useGetRecommendedUpdateLevels() without passing projectId, and adjust a mock comment in mockData.ts. These changes centralize stats logic for the Updates UI and add coverage to ensure correct behavior.
Update useGetRecommendedUpdateLevels to no longer accept a projectId parameter and remove it from the query key. Adjust logging and error messages to be project-agnostic, simplify the enabled condition to rely on mock or auth state, and reformat the hook signature/generics. Add a test import for NULL_PLACEHOLDER and a new test verifying the placeholder renders when value is missing, and clean up an unused comment in an existing test.
Move aggregation and helper logic for update stats into shared utilities and simplify components. StatCard: make value optional and use NULL_PLACEHOLDER from @utils/updates. UpdatesStatsGrid: remove inline aggregation and tooltip/value helpers, use aggregateUpdateStats, getStatValue and getStatTooltipText from @utils/updates and update imports accordingly. This centralizes update-related logic and reduces duplicated code in the grid component.
Rewrite useGetRecommendedUpdateLevels unit tests to centralize and improve mocking. Switch imports to absolute paths, add mocks for logger, @asgardeo/react, MockConfigProvider, and AuthApiContext (mocking useAuthApiClient) instead of stubbing global fetch. Adjust QueryClient defaults (staleTime/gcTime), clear/reset mocks and query client in afterEach, and replace projectId-based hooks with parameterless calls. Update assertions to use Array.isArray, add explicit waitFor timeouts, simplify window.config handling, and rename/extend test timeouts; removed the empty-projectId test.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@apps/customer-portal/webapp/src/api/useGetRecommendedUpdateLevels.ts`:
- Line 29: The JSDoc for useGetRecommendedUpdateLevels contains a stale `@param`
{string} projectId that no longer matches the function signature; update the
JSDoc for the exported hook useGetRecommendedUpdateLevels (or remove the `@param`
line) so the comments reflect its current zero-parameter signature and any
remaining params or return types are accurate.
In
`@apps/customer-portal/webapp/src/components/updates/stat-card-row/UpdatesStatsGrid.tsx`:
- Around line 50-65: The functions renderCountWithSkeleton and
renderExtraContent reference isEffectiveLoading before it's declared, creating a
use-before-declaration smell; move the isEffectiveLoading declaration so it
occurs prior to these function definitions (or alternatively refactor the
functions to accept isEffectiveLoading as an argument) so the variable is
defined when the closures are created; update references to the same symbol
(isEffectiveLoading) inside renderCountWithSkeleton and renderExtraContent to
use the relocated variable (or the new parameter) to eliminate the fragile
ordering.
In `@apps/customer-portal/webapp/src/utils/__tests__/updates.test.ts`:
- Around line 40-43: The Biome lint error comes from mixing optional chaining
with non-null assertions on the test variable `stats`; replace uses like
`stats?.totalUpdatesInstalledBreakdown!.regular` and
`stats?.totalUpdatesPendingBreakdown!.security` with direct property access
since `stats` is already defined in the test (e.g.,
`stats.totalUpdatesInstalledBreakdown.regular` and
`stats.totalUpdatesPendingBreakdown.security`), removing both the `?.` and `!`
operators for all assertions in this test file (`updates.test.ts`).
Move the computed isEffectiveLoading flag earlier in UpdatesStatsGrid to avoid a duplicate declaration and make it available to render helpers. Update unit test to guard against undefined aggregate results (early return) before asserting fields to prevent runtime errors. Remove an outdated @PARAM line from the JSDoc in useGetRecommendedUpdateLevels for minor documentation cleanup.
f196902
into
wso2-open-operations:customer-portal-milestone-1
Description
This pull request refactors the updates statistics functionality in the customer portal webapp by replacing the previous "updates stats" logic with a new "recommended update levels" approach. The changes include renaming files, updating hooks and tests, and removing obsolete components related to the old stats system.
API and Hook Refactor:
Replaced the
useGetUpdatesStatshook withuseGetRecommendedUpdateLevels, updating its implementation to fetch recommended update levels instead of general update stats. The query key, mock data, API endpoint, and response types were all updated accordingly (apps/customer-portal/webapp/src/api/useGetRecommendedUpdateLevels.ts,apps/customer-portal/webapp/src/api/__tests__/useGetRecommendedUpdateLevels.test.tsx). [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]Updated mock function references and test expectations to match the new recommended update levels data structure and logging messages. [1] [2] [3] [4] [5]
Removal of Deprecated Functionality:
useGetProductUpdatesStatshook and its implementation, as well as the associatedUpdatesStatsGridcomponent and its tests, since these are no longer relevant under the new recommended update levels approach (apps/customer-portal/webapp/src/api/useGetProductUpdatesStats.ts,apps/customer-portal/webapp/src/components/updates/UpdatesStatsGrid.tsx,apps/customer-portal/webapp/src/components/updates/__tests__/UpdatesStatsGrid.test.tsx). [1] [2] [3]UI Improvements:
data-testid="error-indicator"attribute to theErrorIndicatorcomponent's icon button for improved testability (apps/customer-portal/webapp/src/components/common/error-indicator/ErrorIndicator.tsx).Test Refactor:
apps/customer-portal/webapp/src/api/__tests__/useGetRecommendedUpdateLevels.test.tsx). [1] [2] [3] [4] [5] [6] [7]These changes streamline the codebase, focus on the new recommended update levels functionality, and remove legacy update stats logic to reduce confusion and maintenance overhead.
Summary by CodeRabbit
Release Notes