[Customer Portal][FE][Web] Add Support Statistics Component and API Integration - #77
Conversation
Introduce SupportPage at apps/customer-portal/webapp/src/pages/SupportPage.tsx. The new React/TypeScript page extracts projectId from the route (useParams), fetches support statistics via useGetProjectSupportStats, and renders CasesOverviewStats with loading and stats props. File includes Apache-2.0 license header.
Remove the sx={{ p: 4 }} prop from the top-level Box in ProjectPage.tsx to eliminate fixed padding around the page. Let the surrounding layout or container handle spacing to avoid redundant whitespace around the project header.
ProjectHub now imports and uses the useGetProjects hook instead of useSearchProjects to fetch project data. No other logic was changed; this aligns the page with the updated/standardized hook for retrieving projects.
Add a new test file for SupportPage (apps/customer-portal/webapp/src/pages/__tests__/SupportPage.test.tsx). Tests use Vitest and React Testing Library and include mocks for react-router useParams, @wso2/oxygen-ui components, oxygen icons, and the useGetProjectSupportStats hook. Includes two tests: one verifies the loading state (skeletons and FileText icon) and one verifies rendered statistics and labels when data is loaded.
Replace mocked hook useSearchProjects with useGetProjects in ProjectHub tests. Update the vi.mock import path and mock variable (mockUseSearchProjects -> mockUseGetProjects) and adjust all mockReturnValue calls to use the new mock so tests align with the renamed API hook.
Introduce ProjectSupportStats to apps/customer-portal/webapp/src/models/responses.ts to model project-level support metrics. The interface includes totalCases, activeChats, sessionChats, and resolvedChats, with JSDoc comments for each field to improve typing and documentation in the customer portal webapp.
Add getMockProjectSupportStats() to apps/customer-portal/webapp/src/models/mockFunctions.ts and import the ProjectSupportStats type. The new function returns randomized ProjectSupportStats (activeChats, resolvedChats, sessionChats, totalCases) for use in UI mock data and testing.
Introduce a new test file for the useGetProjects hook. Adds two tests verifying paginated responses and default pagination behavior using mockProjects, and asserts hook success state. Tests wrap the hook with a QueryClientProvider (retry disabled) and mock the useLogger hook to avoid logging side effects.
Introduce a new test suite for the useGetProjectSupportStats hook using vitest and @testing-library/react. Tests cover initial loading state, successful data fetch, and behavior when no project ID is provided. A mock logger is injected via vi.mock and tests run inside a QueryClientProvider to disable retries. File includes project license header.
Delete the unit test file apps/customer-portal/webapp/src/api/__tests__/useSearchProjects.test.tsx which contained renderHook/react-query tests for useSearchProjects. Removes the corresponding test coverage; no other code changes included in this changeset.
Introduce useGetProjects, a React Query infinite query hook for fetching paginated projects in the customer-portal webapp. Accepts SearchProjectsRequest and a fetchAll flag (uses larger limit and a stable cache key for shared "all projects" queries). Current implementation returns mocked data (mockProjects) with an 800ms simulated latency for development/demo use, uses a default page size of 10 (100 when fetchAll is true), and computes next/previous page offsets; results are cached indefinitely (staleTime: Infinity). Replace or wire this hook to the real backend API when available.
Introduce useGetProjectSupportStats React Query hook (apps/customer-portal/webapp/src/api/useGetProjectSupportStats.ts). The hook returns UseQueryResult<ProjectSupportStats, Error>, uses getMockProjectSupportStats with an 800ms simulated latency, and logs activity via useLogger. Query key is [ApiQueryKeys.SUPPORT_STATS, id], the query is enabled only when id is truthy, and staleTime is set to 5 minutes (TODO: adjust). This is a development/demo mock and should be replaced when wiring to the real backend.
Import the new SupportPage component and update the /support route to render SupportPage instead of reusing ProjectPage. This provides a dedicated page for support-related UI and separates concerns in App.tsx (apps/customer-portal/webapp/src/App.tsx).
Delete apps/customer-portal/webapp/src/api/useSearchProjects.ts. This file implemented a mock-based React Query infinite query hook for searching projects (used mockProjects, simulated ~800ms latency, and returned paginated SearchProjectsResponse). Remove the legacy/mock implementation so callers can rely on a real API-backed hook or alternate implementation.
Fix Header.test.tsx mock import: change vi.mock from '@/api/useSearchProjects' to '@/api/useGetProjects' so the test mocks the correct module (mock implementation unchanged). This aligns the test with the renamed/relocated API hook.
Add apps/customer-portal/webapp/src/constants/supportConstants.ts defining a SupportStatConfig interface and exporting SUPPORT_STAT_CONFIGS. The new file maps ProjectSupportStats keys to icon components, colors, labels and optional secondary icons (imports oxygen-ui icons) and includes the project license header.
Introduce a new SUPPORT_STATS key ("support-stats") in ApiQueryKeys within apps/customer-portal/webapp/src/constants/apiQueryKeys.ts to provide a dedicated query key for project support statistics API calls.
Introduce a new CasesOverviewStats React component to render a responsive grid of support statistic cards. The component accepts isLoading and stats (ProjectSupportStats) props, maps SUPPORT_STAT_CONFIGS to StatCard items, displays a Skeleton while loading, overlays optional secondary icons, and falls back to 0 for missing values. Uses @wso2/oxygen-ui Box, Grid, StatCard and Skeleton components.
Remove unused icon imports (CircleAlert, MessageSquareDiff, MessageSquareMore) from apps/customer-portal/webapp/src/constants/supportConstants.ts to clean up imports and avoid unused symbols.
Add unit tests for CasesOverviewStats at apps/customer-portal/webapp/src/components/support/__tests__/CasesOverviewStats.test.tsx. Mocks @wso2/oxygen-ui and @wso2/oxygen-ui-icons-react, and verifies both loading state (skeletons and icons) and loaded state (renders stat values and labels). Tests use Vitest and React Testing Library.
Replace the React.FC arrow component with an exported default function for ProjectCard. Remove the unused default React import (keeping only the JSX type) and eliminate the redundant export at the file end. Typing and return signature (ProjectCardProps -> JSX.Element) are preserved; no functional changes intended.
Replace the deprecated useSearchProjects hook with useGetProjects in apps/customer-portal/webapp/src/components/header/Header.tsx. Updated the import and hook call to useGetProjects({}, true) so the Header component uses the new project-fetching hook/naming.
Add a mocked useLogger and extend SupportPage tests to validate logging behavior. Introduces mockLogger (debug/error/info/warn) via vi.mock, adjusts the loading test to expect 4 skeletons and persistent icon, adds an error-state test that checks the error message and that mockLogger.error is called with the project id, and asserts mockLogger.debug is called when data loads. This ensures log calls are validated alongside UI expectations.
Introduce logging and error UI for project support stats: add useLogger and useEffect hooks to log fetch errors and successful loads, include isError from useGetProjectSupportStats, and render a centered error message using Box and Typography when fetching fails. Also import the necessary UI components and expand the data fetch handling for better observability and user feedback.
📝 WalkthroughWalkthroughThe PR introduces a fully functional customer portal web application featuring React Router-based navigation, new data-fetching hooks for projects and support statistics, page components (ProjectHub, ProjectPage, SupportPage), header UI components, and comprehensive type definitions and mocks. The placeholder App component is replaced with a routed architecture mapping to multiple titled project pages. Changes
Sequence DiagramsequenceDiagram
actor User
participant Router as React Router
participant Page as Page Component
participant Hook as Data Hook
participant QueryClient as React Query
participant MockAPI as Mock Data Service
participant UI as UI Renderer
User->>Router: Navigate to /projects
Router->>Page: Render ProjectHub
Page->>Hook: useGetProjects()
Hook->>QueryClient: Execute useInfiniteQuery
QueryClient->>MockAPI: Fetch projects (simulated 800ms)
MockAPI-->>QueryClient: Return SearchProjectsResponse
QueryClient-->>Hook: Return paginated data
Hook-->>Page: Pass projects, loading state
Page->>UI: Render ProjectCard grid
UI-->>User: Display projects
User->>Router: Click project → /project-1/support
Router->>Page: Render SupportPage
Page->>Hook: useGetProjectSupportStats(projectId)
Hook->>QueryClient: Execute useQuery
QueryClient->>MockAPI: Fetch support stats (simulated 800ms)
MockAPI-->>QueryClient: Return ProjectSupportStats
QueryClient-->>Hook: Return stats data
Hook-->>Page: Pass stats, loading state
Page->>UI: Render CasesOverviewStats
UI-->>User: Display support metrics
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ 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.
Actionable comments posted: 7
🤖 Fix all issues with AI agents
In
`@apps/customer-portal/webapp/src/api/__tests__/useGetProjectSupportStats.test.tsx`:
- Around line 32-43: The QueryClient is shared across tests causing cache
pollution and flakiness; fix by creating/resetting a fresh QueryClient before
each test using vitest's beforeEach (imported from 'vitest') and re-creating the
wrapper that uses that QueryClient; specifically, move the QueryClient
construction and the wrapper ({ children } => <QueryClientProvider
client={queryClient}>{children}</QueryClientProvider>) into a beforeEach so
queryClient is new per test (or call queryClient.clear() in beforeEach) to
ensure cached responses from previous tests don't affect
useGetProjectSupportStats tests.
In `@apps/customer-portal/webapp/src/components/header/Actions.tsx`:
- Around line 37-45: The Join our community Button that uses JOIN_COMMUNITY_URL
should open the external site in a new tab and prevent reverse tabnabbing:
update the Button (in Actions.tsx where Button is rendered with startIcon and
href={JOIN_COMMUNITY_URL}) to include target="_blank" and rel="noopener
noreferrer" attributes so the external page cannot access window.opener.
In `@apps/customer-portal/webapp/src/components/header/Header.tsx`:
- Around line 110-133: The effect re-runs every render because the derived
projects array (created via flatMap) has a new reference each render; memoize
that array with useMemo (e.g., const memoizedProjects = useMemo(() =>
projectsFlatMapLogic..., [rawProjectsDep1, rawProjectsDep2])) and then replace
projects with memoizedProjects in the useEffect dependency list (keeping
projectId and selectedProject?.id), or alternatively remove the derived array
from deps and depend on the raw source used to build it; update references in
the effect to use memoizedProjects and keep setProject, projectId,
selectedProject checks unchanged.
In `@apps/customer-portal/webapp/src/components/projectCard/ProjectCard.tsx`:
- Around line 78-88: The component ProjectCard currently uses mock functions
getMockStatus, getMockOpenCases, and getMockActiveChats as default parameter
values for props (status, openCases, activeChats); remove these mock defaults
and replace them with sensible static defaults (e.g., status = "Unknown",
openCases = 0, activeChats = 0) or make the props required in ProjectCardProps
so callers must pass real values, then delete the now-unused
getMockStatus/getMockOpenCases/getMockActiveChats imports; update the function
signature in ProjectCard to use the new defaults or required types and adjust
any callers/tests that relied on the mocks.
In `@apps/customer-portal/webapp/src/components/sideNavBar/SideBar.tsx`:
- Around line 78-91: The Link generation currently interpolates projectId (typed
as projectId?: string) into the URL which can produce "/undefined/..." during
transitions; update the Sidebar rendering logic around the Link that uses
NavigateLink so it only renders the Link (or a non-clickable fallback) when
projectId is defined. Locate the block using projectId in SideBar.tsx (the Link
with component={NavigateLink} and to={`/${projectId}/${item.path}`}) and wrap it
with a guard like "if (!projectId) return null" or render an inert element (no
NavigateLink) to avoid creating malformed links; ensure Sidebar.Item,
Sidebar.ItemIcon, and Sidebar.ItemLabel are preserved in the fallback so layout
remains stable.
In `@apps/customer-portal/webapp/src/pages/__tests__/SupportPage.test.tsx`:
- Around line 27-35: Tests share the mockLogger instance so its vi.fn() call
counts persist across tests; add a beforeEach to reset mocks (e.g.,
vi.clearAllMocks()) after the mock definitions to ensure each test starts with
fresh mock state. Locate the mockLogger and useLogger mock setup and insert a
beforeEach that clears the mocks so assertions like the loading state test's
check of mockLogger.debug are reliable.
In `@apps/customer-portal/webapp/src/pages/ProjectPage.tsx`:
- Line 41: The current destructuring const { projectId } = useParams<{
projectId: string }>() can yield undefined; update ProjectPage to defensively
handle missing projectId by adding an early guard after that line (e.g., if
(!projectId) return an error/NotFound UI or redirect) and ensure any downstream
uses of projectId (data fetching hooks, render logic) only run when projectId is
present; modify affected functions/components in this file (references to
projectId, fetchProject, useEffect hooks) to expect a defined string or be
skipped until projectId exists.
🧹 Nitpick comments (19)
apps/customer-portal/webapp/src/components/header/SearchBar.tsx (1)
25-33: Confirm accessible labeling for the search input.
IfSearchBarUIdoesn’t render a label, add an explicitaria-label(or equivalent) to avoid a placeholder-only label.♿️ Proposed tweak (if supported by the component)
<SearchBarUI size="small" placeholder="Search cases, tickets, or users" + aria-label="Search" sx={{ minWidth: 400 }} />apps/customer-portal/webapp/src/components/projectCard/ProjectCardSkeleton.tsx (1)
33-33: Consider using function declaration for consistency.Other components in this PR (e.g.,
ProjectCardActions,Brand) use function declarations (function ComponentName()) while this uses an arrow function. Consider aligning the style for consistency across the codebase.♻️ Optional: Convert to function declaration
-const ProjectCardSkeleton = (): JSX.Element => { - return ( +export default function ProjectCardSkeleton(): JSX.Element { + return ( <Card ... ); -}; - -export default ProjectCardSkeleton; +}apps/customer-portal/webapp/src/utils/projectCard.ts (2)
35-37: Consider i18n for date formatting.The locale is hardcoded to
"en-US". If the application needs to support internationalization, consider using the user's locale or a configurable locale setting.Example using browser locale
- const month = date.toLocaleString("en-US", { month: "short" }); + const month = date.toLocaleString(undefined, { month: "short" });Or use a centralized i18n configuration if available.
51-64: Consider extracting status strings to constants.The status strings (
"All Good","Need Attention","Critical Issues") are hardcoded. If these are used elsewhere or could change, extracting them to a shared constants file would improve maintainability.apps/customer-portal/webapp/src/components/support/CasesOverviewStats.tsx (1)
85-91: Unsafe type cast for loading skeleton.The
Skeletoncomponent is cast tounknown as numberto satisfyStatCard'svalueprop type. This is a type safety workaround that could mask issues.Consider whether
StatCardshould acceptReactNodefor itsvalueprop, or if a dedicated loading state prop would be cleaner.Alternative approaches
Option 1: If you control
StatCard, update itsvalueprop to acceptReactNode:interface StatCardProps { value: number | ReactNode; // ... }Option 2: Use a wrapper component that handles loading internally:
<StatCard label={stat.label} value={stats?.[stat.key] ?? 0} icon={<stat.icon />} iconColor={stat.iconColor} isLoading={isLoading} />apps/customer-portal/webapp/src/constants/appLayoutConstants.ts (1)
33-50: Consider exportingAppShellNavIteminterface.The
AppShellNavIteminterface is not exported. If consumer components (likeSideBar) need to type their props based on this interface, exporting it would improve type safety.Proposed change
-interface AppShellNavItem { +export interface AppShellNavItem {apps/customer-portal/webapp/src/components/header/UserProfile.tsx (1)
39-49: Consider removing unnecessary fragment wrapper.The fragment
<></>wrapper is unnecessary sinceUserMenuis the only child element. You can return theUserMenudirectly.♻️ Suggested simplification
return ( - <> - {/* user profile menu */} - <UserMenu - user={mockUser} - onProfileClick={() => logger.debug("Profile clicked")} - onSettingsClick={() => logger.debug("Settings clicked")} - onLogout={() => navigate("/")} - /> - </> + <UserMenu + user={mockUser} + onProfileClick={() => logger.debug("Profile clicked")} + onSettingsClick={() => logger.debug("Settings clicked")} + onLogout={() => navigate("/")} + /> );apps/customer-portal/webapp/src/models/mockData.ts (1)
79-84: Minor: Email capitalization inconsistency.The mock email
"John@example.com"uses an unusual capitalization. While email addresses are case-insensitive for the domain part, it's conventional to use lowercase for mock/test data to avoid potential issues with case-sensitive comparisons in tests or UI displays.♻️ Suggested fix
export const mockUser: UserProfile = { name: "John Doe", - email: "John@example.com", + email: "john@example.com", avatar: "JD", role: "Admin", };apps/customer-portal/webapp/src/pages/ProjectHub.tsx (4)
63-67: Consider logging only on initial load, not on every re-render.The debug log will fire every time
projects.lengthorloggerchanges. Sinceprojectsis re-derived on each render viaflatMap, this effect could fire more frequently than intended. Consider using a ref to track if logging has occurred to avoid duplicate logs.♻️ Suggested improvement using a ref
+import { useEffect, useRef, type JSX } from "react"; -import { useEffect, type JSX } from "react"; ... + const hasLoggedProjects = useRef(false); + useEffect(() => { - if (projects.length > 0) { + if (projects.length > 0 && !hasLoggedProjects.current) { logger.debug(`${projects.length} projects loaded in ProjectHub`); + hasLoggedProjects.current = true; } - }, [projects.length, logger]); + }, [projects.length, logger]);
74-108: Extract duplicated responsive grid styles.The responsive flex styling for the card grid is duplicated between the loading skeleton and the loaded state (lines 76-106 and lines 134-172). Consider extracting this to a shared style constant or a wrapper component to improve maintainability.
♻️ Suggested approach
const cardGridSx = { display: "flex", flexWrap: "wrap", justifyContent: "center", gap: 3, maxWidth: 1800, mx: "auto", width: "100%", }; const cardWrapperSx = { flex: { xs: "1 1 100%", sm: "0 1 calc(50% - 24px)", md: "0 1 calc(33.33% - 24px)", lg: "0 1 calc(25% - 24px)", xl: "0 1 calc(20% - 24px)", }, maxWidth: { xs: "100%", sm: 400 }, minWidth: 300, };Then reuse
cardGridSxandcardWrapperSxin both the loading and loaded states.Also applies to: 133-174
111-120: Stale comment: "Log error if projects are not loaded."The comment says "Log error" but this block renders the error UI, not logging. The logging is handled in the earlier
useEffect. Update the comment to reflect the actual behavior.✏️ Suggested fix
- /** - * Log error if projects are not loaded. - */ + /** + * Render error state if loading failed. + */ if (isError) {
122-131: Stale comment: "Log error if projects are not loaded."Same issue—this comment describes logging but the code renders an empty state message. Update to match the actual behavior.
✏️ Suggested fix
- /** - * Log error if projects are not loaded. - */ + /** + * Render empty state if no projects are available. + */ if (!projects || projects.length === 0) {apps/customer-portal/webapp/src/pages/SupportPage.tsx (1)
38-47: Consider handling missingprojectIdexplicitly.When
projectIdisundefined, passing an empty string disables the query (viaenabled: !!idin the hook), but the component will show an indefinite loading state rather than an error. Consider adding an explicit check for missingprojectIdto provide a clearer user experience.♻️ Suggested improvement
+ if (!projectId) { + return ( + <Box sx={{ mt: 3, textAlign: "center" }}> + <Typography variant="h6" color="error"> + Project ID is required. + </Typography> + </Box> + ); + } + const { data: stats, isLoading, isError, - } = useGetProjectSupportStats(projectId || ""); + } = useGetProjectSupportStats(projectId);apps/customer-portal/webapp/src/components/header/__tests__/Header.test.tsx (2)
106-120: Consider renamingmockUseSearchProjectstomockUseGetProjectsfor clarity.The mock variable is named
mockUseSearchProjectsbut it mocks theuseGetProjectshook (as seen in the vi.mock on line 117). This inconsistency could cause confusion during maintenance.🔧 Suggested rename
-const mockUseSearchProjects = vi.fn(() => ({ +const mockUseGetProjects = vi.fn(() => ({ data: { pages: [{ projects: mockProjects }], }, fetchNextPage: mockFetchNextPage, hasNextPage: false, isFetchingNextPage: false, isError: false, })) as any; vi.mock("@/api/useGetProjects", () => ({ default: (searchData: any, fetchAll: any) => - mockUseSearchProjects(searchData, fetchAll), + mockUseGetProjects(searchData, fetchAll), }));Also update references in
beforeEach(line 169) and test cases (lines 247, 261).
235-244: Test case provides limited coverage for the useEffect clearing logic.The comment on lines 241-242 acknowledges that the switcher isn't rendered on the hub page, so this test only verifies that the switcher is absent. It doesn't directly verify the
useEffectstate-clearing logic mentioned in the comment. Consider adding a test that renders on a project page first, then simulates navigation to the hub to verify state transitions.apps/customer-portal/webapp/src/components/projectCard/ProjectCardStats.tsx (1)
95-97: Hardcoded color may not adapt to theme changes.Using
colors.blue[500]directly bypasses the theme system. If the app supports light/dark modes or custom themes, this color won't adjust accordingly. Consider using a theme-aware color like"info.main"or"secondary.main"for consistency.🎨 Theme-aware alternative
- <Typography variant="body2" color={colors.blue[500]}> + <Typography variant="body2" color="info.main"> {activeChats} </Typography>apps/customer-portal/webapp/src/models/mockFunctions.ts (1)
63-70: Inconsistent mock data generation approach.
getMockProjectSupportStatsuses inlineMath.random()with hardcoded ranges, while the other functions (getMockStatus,getMockOpenCases,getMockActiveChats) select from predefined arrays inmockData.ts. For consistency and easier maintenance, consider defining similar option arrays for support stats or extracting a shared random selection helper.apps/customer-portal/webapp/src/components/sideNavBar/SideBar.tsx (1)
59-64:activeItemextraction may fail for trailing slashes.
location.pathname.split("/").pop()returns an empty string for paths like/projectId/dashboard/, causingactiveItemto default to"dashboard"via the|| "dashboard"fallback. However, this fallback may mask actual navigation issues. Consider using a more robust approach:🔧 More robust path extraction
- const activeItem = location.pathname.split("/").pop() || "dashboard"; + const pathSegments = location.pathname.split("/").filter(Boolean); + const activeItem = pathSegments[pathSegments.length - 1] || "dashboard";apps/customer-portal/webapp/src/api/useGetProjects.ts (1)
36-48:searchDataparameter is accepted but not used in the query function.The hook accepts
searchData: SearchProjectsRequestbut thequeryFndoesn't filter or search based on it — it always returns slicedmockProjects. This is fine for the mock implementation, but consider adding a TODO comment to remind developers to implement actual search/filter logic when wiring to the real API.📝 Add TODO for future implementation
/** * Mock behavior: simulate network latency for the in-memory `mockProjects` data. * This is intended only for development/demo use and should be removed or * replaced when wiring this hook to the real backend API. + * + * TODO: Implement actual search/filter logic using `searchData` when + * connecting to the real backend API. */
There was a problem hiding this comment.
Pull request overview
This pull request adds API integration for project support statistics and implements case statistics cards with loading states. The changes introduce routing with react-router, create support statistics components, and add comprehensive test coverage.
Changes:
- Added react-router integration for navigation between project hub and project-specific pages
- Implemented API hooks for fetching projects and support statistics with mock data
- Created case overview statistics cards with loading skeletons and error handling
Reviewed changes
Copilot reviewed 59 out of 60 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Added react-router and @asgardeo/react-router dependencies |
| vite.config.ts | Configured vitest with CSS support and dependency inlining |
| src/utils/projectCard.ts | Utility functions for date formatting and status color mapping |
| src/models/responses.ts | TypeScript interfaces for API response types |
| src/models/requests.ts | TypeScript interfaces for API request types |
| src/models/mockData.ts | Mock data for projects and user profiles |
| src/models/mockFunctions.ts | Helper functions for generating mock statistics |
| src/constants/*.ts | Application constants for API keys, navigation, and support stats |
| src/api/*.ts | React Query hooks for fetching projects and support statistics |
| src/components/support/* | Support statistics display components with loading states |
| src/components/projectCard/* | Project card components with skeleton loading |
| src/components/header/* | Header components including search, project switcher, and user profile |
| src/components/footer/Footer.tsx | Application footer with company info and links |
| src/components/sideNavBar/* | Sidebar navigation with subscription widget |
| src/pages/*.tsx | Page components for support, project details, and project hub |
| src/layouts/AppLayout.tsx | Main layout with conditional sidebar rendering |
| src/App.tsx | Application routing configuration |
| All test files | Comprehensive unit tests for components, utilities, and API hooks |
Files not reviewed (1)
- apps/customer-portal/webapp/pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b610cdc
into
wso2-open-operations:customer-portal-milestone-1
Purpose
This pull request implements the API integration for the
projects/{id}/stats/supportendpoint to fetch and display support case statistics. It also introduces a dedicated statistics row in the UI with high-fidelity loading skeletons to enhance the user experience during data fetching.ScreenRecordings and screenshots
Screen.Recording.2026-01-31.at.20.24.48.mov
Support Page Stats cards
**Light mode: **
**Dark mode: **
**Loading Skeletons: - Light mode: **
**Loading Skeletons: -Dark mode: **

**Loading Skeletons: -Animations: **
Screen.Recording.2026-01-31.at.20.28.12.mov
Goals
Approach
useGetProjectSupportStatscustom hook. It includes a simulated 800ms latency to test the UI's resilience and loading states, integrated with a logging mechanism for monitoring.CasesOverviewStats.tsxcomponent using a responsive grid layout. Each stat is housed in a card that handles its own internal loading and error states.Skeletoncomponents from Oxygen UI that mirror the exact dimensions of the stat cards to prevent layout shift.User stories
Automation tests
useGetProjectSupportStatshook to verify data mapping and error handling.CasesOverviewStatsto ensure skeletons render during loading states and data displays correctly upon success.projectIdfrom the route context to fetch the relevant stats.Closes(#57)
Summary by CodeRabbit
New Features
Tests