Skip to content

[Customer Portal][FE][Web] Project Structure Refactor and Project Switcher Enhancements - #80

Merged
Rashmika998 merged 23 commits into
wso2-open-operations:customer-portal-milestone-1from
dileepapeiris:feat/add-project-selector-dropdown
Feb 4, 2026
Merged

Rashmika998 merged 23 commits into
wso2-open-operations:customer-portal-milestone-1from
dileepapeiris:feat/add-project-selector-dropdown

Conversation

@dileepapeiris

@dileepapeiris dileepapeiris commented Jan 31, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

This PR standardizes the project's component architecture by centralizing shared layout elements and organizing feature-specific components. Additionally, it enhances the Header navigation by implementing a robust Project Switcher dropdown with advanced loading states and detailed project information.

ScreenRecordings and screenshots

Screen.Recording.2026-01-31.at.22.14.13.mov

Projects Selector

**Light mode: **
image

**Dark mode: **
image

**Loading Skeletons: **

**Light mode: **
image

**Dark mode: **
image

** Animations**

Screen.Recording.2026-01-31.at.22.14.13.mov

Goals

  • Architectural Cleanup: Establish a logical separation between global layout components and feature-specific logic.
  • Enhanced Project Switching: Improve the project selection experience by displaying both Project Name and Project Key within the dropdown.
  • Visual Stability: Eliminate layout jumping by using high-fidelity skeleton loaders for the project dropdown and ensuring contextual icons remain persistent.

Approach

  • Directory Structure Revamp:
    • Centralized Common Components: Moved header, footer, and sideNavBar folders from src/components/ to a new src/components/common/ directory.
    • Feature Grouping: Relocated projectCard from src/components/ to src/components/projectHub/projectCard.
    • Global Path Update: Performed a comprehensive refactor of import paths across the entire src directory.
  • Project Switcher Implementation:
    • Detailed Menu Items: Refactored the dropdown menu to display both the Project Name and Project Key for better identification.
    • Skeleton Loaders: Integrated a Skeleton component into the ProjectSwitcher that mimics the exact button dimensions, border-radius, and typography height to ensure zero layout shift during data fetching.
    • Contextual Persistence: Configured the FolderOpen icon to remain visible even during the loading state, providing immediate visual feedback upon page refresh.

User stories

  • Navigation Clarity: As a user, I can see both the name and unique key of my projects in the switcher, helping me distinguish between similar project names.
  • Seamless Loading: As a user, I see a polished placeholder skeleton while my project list is being retrieved, preventing the UI from shifting or appearing broken.
  • Contextual Awareness: As a user, I see the "Project" icon immediately, so I know where the switcher is located even before the data arrives.

Changes

Cohort File(s) Summary
Directory Refactor src/components/common/*, src/components/projectHub/* Relocated core layout and feature-specific components; updated global imports.
Project Switcher UI ProjectSwitcher.tsx, ProjectSwitcher.test.tsx Implemented dropdown with Name/Key display and integrated skeleton loading states.
Header Enhancements Header.tsx, Actions.tsx Updated Header to support the new switcher logic and persistent icon context.
Utilities/Mock mockData.ts, responses.ts Updated mock data to include project keys for testing the new switcher display.

Automation tests

  • Unit tests:
    • Verified that ProjectSwitcher correctly renders both the Project Name and Key.
    • Verified that the Skeleton component is rendered when the isLoading prop is true.
    • Updated path references for all component unit tests to match the new common/ directory structure.
  • Visual Regression: Manually verified that the transition from Skeleton -> Loaded State maintains the same height and width to prevent layout shifts.
image

Summary by CodeRabbit

Release Notes

  • New Features
    • Added customizable header with search, theme toggle, community link, and user profile menu
    • Added footer with company information and policy links
    • Added navigation sidebar with collapsible menu and subscription widget
    • Added project hub page with project cards displaying status, cases, and chats
    • Added project switcher in header for quick project navigation

Introduce a new Footer component that wraps @wso2/oxygen-ui's Footer and uses app layout constants for company name, terms, and privacy URLs. Add unit tests for the Footer, and new tests for Header subcomponents (Actions and Brand). Tests mock @wso2/oxygen-ui and icon packages to verify rendered links, theme toggle, user profile, logo, and product title.
Refactor import paths to match reorganized component folders: Header/Footer/SideBar imports moved to components/common and ProjectCard/ProjectCardSkeleton imports moved to components/projectHub. Updated corresponding tests' mocks to use the new paths. No functional changes, just path updates to reflect project structure.
Introduce modular ProjectCard UI pieces for the customer portal: ProjectCardBadges, ProjectCardInfo, ProjectCardStats, and ProjectCardSkeleton. Each component is a TypeScript React component using @wso2/oxygen-ui primitives and typed props (JSX). Badges renders project key and status (uses getStatusColor), Info shows title and truncated subtitle, Stats displays open cases, active chats and formatted date (uses formatProjectDate) with icons, and Skeleton provides a loading placeholder matching the card layout. These additions split the ProjectCard into reusable parts and improve consistency of layout and loading state.
Delete unit tests for ProjectCard and its subcomponents. Removed files:
- apps/customer-portal/webapp/src/components/projectCard/__tests__/ProjectCard.test.tsx
- apps/customer-portal/webapp/src/components/projectCard/__tests__/ProjectCardActions.test.tsx
- apps/customer-portal/webapp/src/components/projectCard/__tests__/ProjectCardBadges.test.tsx
- apps/customer-portal/webapp/src/components/projectCard/__tests__/ProjectCardInfo.test.tsx

These tests contained mocks for @wso2/oxygen-ui, react-router, and internal utilities and have been removed (likely as part of test cleanup or refactor).
Delete four ProjectCard components (ProjectCardActions, ProjectCardBadges, ProjectCardInfo, ProjectCardSkeleton) from apps/customer-portal/webapp/src/components/projectCard. Remove associated UI pieces; update any imports/usages referencing these files.
Introduce unit tests for Header, ProjectSwitcher, SearchBar, and UserProfile components under apps/customer-portal/webapp/src/components/common/header/__tests__/. Tests cover rendering, project selection/navigation, sidebar toggle behavior, fetchNextPage invocation handling, and error cases. The tests include extensive mocking for @wso2/oxygen-ui, icons, react-router hooks, logger hook, projects API, and internal subcomponents to isolate behavior.
Delete SideBar.tsx and SubscriptionWidget.tsx along with their unit tests from apps/customer-portal/webapp/src/components/sideNavBar. These files were removed as part of a sidebar cleanup/refactor to consolidate or replace the side navigation functionality.
Delete Footer implementation and its tests from apps/customer-portal/webapp, and remove header test files. Removed files: Footer.tsx, footer/Footer.test.tsx, header/__tests__/Actions.test.tsx, header/__tests__/Brand.test.tsx as part of a component/test cleanup.
Delete legacy header implementation from the customer-portal webapp. Removed files: Actions.tsx, Brand.tsx, Header.tsx, ProjectSwitcher.tsx, and SearchBar.tsx from apps/customer-portal/webapp/src/components/header. These deletions remove the previous header, brand, actions, project switcher, and search bar components (presumably to be replaced by a refactored header implementation).
Delete ProjectCard, ProjectCardStats and UserProfile components and their associated tests (ProjectCardSkeleton.test.tsx, ProjectCardStats.test.tsx) from apps/customer-portal/webapp/src/components. Cleans up obsolete project-card and header UI pieces and corresponding unit tests.
Introduce a new SubscriptionWidget component for the side nav (uses oxygen-ui components and Crown icon; returns null when collapsed). Update Header tests: adjust the mocked ProjectSwitcher to accept an isLoading prop (adds data-loading attribute) and add a test to verify Header passes isLoading when project search is loading. Remove legacy tests under components/header/__tests__ that were replaced/relocated to the common/header test suite.
Add four new header components for the customer portal: Brand, Actions, Header, and ProjectSwitcher. Brand renders the WSO2 logo and title; Actions provides the Join Community button, theme toggle, divider, and user profile. ProjectSwitcher renders a ComplexSelect with a loading skeleton and handles project selection. Header composes these parts, fetches projects via useGetProjects, keeps selected project state in sync with the URL, and navigates to the selected project's subpath.
Introduce Header components SearchBar and UserProfile and add unit tests for the sidebar. SearchBar uses @wso2/oxygen-ui SearchBar with a small size and placeholder; UserProfile wraps @wso2/oxygen-ui UserMenu, uses mockUser, useNavigate for logout navigation and useLogger for click handlers. Add tests for SideBar and SubscriptionWidget that mock @wso2/oxygen-ui, icons and react-router to assert navigation items render, links include projectId, and subscription widget behavior when collapsed or expanded. All new files include the project Apache 2.0 license header.
Introduce a new SideBar component for the customer portal using @wso2/oxygen-ui and react-router. The component supports collapsed state, expanded menus, selection/expand callbacks, and renders navigation items from APP_SHELL_NAV_ITEMS plus a footer with SubscriptionWidget and Settings link.

Add unit tests for ProjectCard and its subcomponents (ProjectCardActions, ProjectCardBadges, ProjectCardInfo) using Vitest and React Testing Library. Tests mock @wso2/oxygen-ui, icons, utilities, and react-router to verify rendering, props, behavior (including navigation and callback handling), and visual chip color logic.
Introduce a new ProjectCard component and unit tests. Adds ProjectCard.tsx which renders project badges, info, stats and actions, accepts explicit props (id, projectKey, title, subtitle, date, optional status/openCases/activeChats and onViewDashboard), uses mock defaults for status/counts, and navigates to a dashboard route when clicked. Also adds tests: ProjectCardSkeleton.test.tsx (verifies skeleton layout and placeholders, mocks @wso2/oxygen-ui) and ProjectCardStats.test.tsx (verifies counts, formatted date and icons, mocks UI icons and utils). Includes license headers in new files.
Introduce a new ProjectCardActions component at apps/customer-portal/webapp/src/components/projectHub/projectCard/ProjectCardActions.tsx. The component renders card action area using Form.CardActions and Stack from @wso2/oxygen-ui and includes a full-width outlined secondary Button labeled "View Dashboard" with an ArrowRight icon. File includes license header and basic styling (spacing, padding) for consistent layout within project cards.
@coderabbitai

coderabbitai Bot commented Jan 31, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR introduces a complete customer portal UI layout with header, footer, and sidebar navigation components, a project hub page with project card cards, and comprehensive test coverage. All changes are new component additions integrating Oxygen UI library.

Changes

Cohort / File(s) Summary
Header Components
apps/customer-portal/webapp/src/components/common/header/Brand.tsx, apps/customer-portal/webapp/src/components/common/header/Actions.tsx, apps/customer-portal/webapp/src/components/common/header/SearchBar.tsx, apps/customer-portal/webapp/src/components/common/header/UserProfile.tsx, apps/customer-portal/webapp/src/components/common/header/ProjectSwitcher.tsx
Five new header sub-components: Brand displays logo and title, Actions renders community button, theme toggle, and user profile, SearchBar provides a search input, UserProfile shows user menu with nav/logout handlers, ProjectSwitcher renders project selection dropdown with loading state and onChange callback.
Header Tests
apps/customer-portal/webapp/src/components/common/header/__tests__/Brand.test.tsx, apps/customer-portal/webapp/src/components/common/header/__tests__/Actions.test.tsx, apps/customer-portal/webapp/src/components/common/header/__tests__/SearchBar.test.tsx, apps/customer-portal/webapp/src/components/common/header/__tests__/UserProfile.test.tsx, apps/customer-portal/webapp/src/components/common/header/__tests__/ProjectSwitcher.test.tsx, apps/customer-portal/webapp/src/components/common/header/__tests__/Header.test.tsx
Comprehensive unit tests validating header component rendering, callbacks, loading states, and routing integration. Header.test.tsx includes extensive mocking and tests for conditional rendering, project switching, and sidebar toggle.
Header Integration
apps/customer-portal/webapp/src/components/common/header/Header.tsx
Modified Header component to pass loading state from useGetProjects hook to ProjectSwitcher component.
Footer Components
apps/customer-portal/webapp/src/components/common/footer/Footer.tsx, apps/customer-portal/webapp/src/components/common/footer/__tests__/Footer.test.tsx
Footer component wiring company name and policy URLs from constants. Test validates rendering of company name and policy links.
SideBar Components
apps/customer-portal/webapp/src/components/common/sideNavBar/SideBar.tsx, apps/customer-portal/webapp/src/components/common/sideNavBar/SubscriptionWidget.tsx
SideBar renders navigation items with active state detection and subscription widget in footer. SubscriptionWidget displays subscription info with conditional rendering based on collapsed state.
SideBar Tests
apps/customer-portal/webapp/src/components/common/sideNavBar/__tests__/SideBar.test.tsx, apps/customer-portal/webapp/src/components/common/sideNavBar/__tests__/SubscriptionWidget.test.tsx
Tests verify sidebar navigation rendering with projectId route construction, settings link, and subscription widget. SubscriptionWidget test validates collapsed/expanded rendering states.
ProjectCard Components
apps/customer-portal/webapp/src/components/projectHub/projectCard/ProjectCard.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/ProjectCardActions.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/ProjectCardBadges.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/ProjectCardInfo.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/ProjectCardStats.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/ProjectCardSkeleton.tsx
Six sub-components composing ProjectCard: Actions renders view dashboard button, Badges displays project key and status, Info shows title/subtitle, Stats renders open cases/active chats/date, Skeleton provides loading placeholder, and ProjectCard orchestrates all with navigation and optional mock data.
ProjectCard Tests
apps/customer-portal/webapp/src/components/projectHub/projectCard/__tests__/ProjectCard.test.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/__tests__/ProjectCardActions.test.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/__tests__/ProjectCardBadges.test.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/__tests__/ProjectCardInfo.test.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/__tests__/ProjectCardStats.test.tsx, apps/customer-portal/webapp/src/components/projectHub/projectCard/__tests__/ProjectCardSkeleton.test.tsx
Tests validate component rendering, data presentation, icon presence, and navigation behavior for all ProjectCard sub-components.
Layout & Pages
apps/customer-portal/webapp/src/layouts/AppLayout.tsx, apps/customer-portal/webapp/src/pages/ProjectHub.tsx
AppLayout orchestrates Header, SideBar, Footer with conditional sidebar rendering based on current path. ProjectHub page fetches projects, handles pagination, manages loading/error states, and renders grid of ProjectCard components with comprehensive error/loading/empty handling.
Layout & Page Tests
apps/customer-portal/webapp/src/layouts/__tests__/AppLayout.test.tsx, apps/customer-portal/webapp/src/pages/__tests__/ProjectHub.test.tsx
AppLayout test validates conditional component rendering and event wiring. ProjectHub test covers loading/error/empty states, data rendering, and logger integration.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

Type/Task

Suggested reviewers

  • v15a1
  • Rashmika998
  • cloby99

Poem

🐰 A rabbit hops through component trees,
Header, footer, sidebar with ease,
Project cards dance in gridded delight,
Layout flows smooth, all tests burning bright!
Buttons and badges, icons so fine,
Portal UI perfection — hopping divine! 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: project structure refactor and project switcher enhancements, which are the primary objectives of the PR.
Description check ✅ Passed The description covers all essential template sections including Purpose, Goals, Approach, User stories, Automation tests, and includes UI screenshots. Minor sections like Documentation and Training are reasonably omitted given the internal architecture nature.
Docstring Coverage ✅ Passed Docstring coverage is 89.47% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Use project.key as the secondary text in ProjectSwitcher (replacing project.description). Update tests to mock MenuItem.Text to render both primary and secondary and assert that both project.name and project.key are present (using non-exact matching).
@dileepapeiris
dileepapeiris requested a review from Copilot January 31, 2026 16:16
@dileepapeiris dileepapeiris self-assigned this Jan 31, 2026
@dileepapeiris dileepapeiris added Type/Improvement Marks enhancements or improvements to existing features Type/UX Refers to user experience-related tasks or issues App/Customer Portal Area/Frontend labels Jan 31, 2026
@dileepapeiris dileepapeiris moved this from Todo to Done in Customer Portal Development Jan 31, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the customer portal project structure by centralizing common layout components (header, footer, sideNavBar) into a src/components/common/ directory and organizing feature-specific components (projectCard) into src/components/projectHub/. It implements comprehensive routing with React Router 7, adds a ProjectSwitcher component with skeleton loading states, and creates several new pages including ProjectHub, ProjectPage, and SupportPage with their corresponding API hooks and mock data infrastructure.

Changes:

  • Centralized common layout components into src/components/common/ directory structure
  • Implemented React Router 7 for application routing with project-specific and hub pages
  • Added ProjectSwitcher component with skeleton loading states and project name/key display
  • Created support statistics infrastructure with API hooks, mock data, and UI components

Reviewed changes

Copilot reviewed 59 out of 60 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
vite.config.ts Added CSS support and inline dependencies for Oxygen UI components in test environment
src/utils/projectCard.ts New utility functions for date formatting and status color mapping
src/utils/tests/projectCard.test.ts Comprehensive tests for projectCard utilities
src/utils/tests/logger.test.ts New tests for Logger functionality with various log levels
src/pages/* New page components: SupportPage, ProjectPage, ProjectHub with tests
src/models/* New type definitions for requests/responses and mock data infrastructure
src/layouts/AppLayout.tsx Main layout component with conditional sidebar rendering
src/components/common/header/* Header components including ProjectSwitcher, Brand, Actions, SearchBar, UserProfile
src/components/common/footer/* Footer component with company info and policy links
src/components/common/sideNavBar/* Sidebar navigation and subscription widget
src/components/projectHub/projectCard/* ProjectCard components with badges, info, stats, actions, and skeleton
src/components/support/* CasesOverviewStats component for displaying support metrics
src/constants/* Application constants for API keys, layout navigation, and support config
src/api/* API hooks for fetching projects and support statistics
src/App.tsx Main routing configuration with React Router 7
package.json Added react-router and @asgardeo/react-router dependencies
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.

Comment thread apps/customer-portal/webapp/src/components/common/header/Header.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@apps/customer-portal/webapp/src/api/useGetProjects.ts`:
- Around line 59-60: The pagination offset from callers
(searchData.pagination?.offset) is being ignored; update useGetProjects.ts so
initialPageParam and the queryFn honor that requested offset: set
initialPageParam to searchData.pagination?.offset ?? 0 (instead of hardcoded 0)
and ensure the queryFn signature for pageParam uses that value (e.g., defaulting
to the incoming pageParam and passing it through to the underlying search API),
so requests start at the requested non-zero offset; reference symbols:
initialPageParam, queryFn, and searchData.pagination?.offset.

In
`@apps/customer-portal/webapp/src/components/projectHub/projectCard/ProjectCard.tsx`:
- Around line 78-86: The component ProjectCard currently calls getMockStatus,
getMockOpenCases, and getMockActiveChats directly in the props default values
which re-runs on every render and changes those values; fix this by moving mock
generation to a stable initializer — either call the mock generators once
outside the component (module-level constants) or initialize them inside
ProjectCard using a stable hook (useMemo or useRef/useState with an empty
dependency array) and then use those stable values as the final
status/openCases/activeChats when real props are not provided; update references
to getMockStatus, getMockOpenCases, and getMockActiveChats to use the stabilized
values.

In `@apps/customer-portal/webapp/src/layouts/__tests__/AppLayout.test.tsx`:
- Around line 28-31: The test's mockShellState uses expandedMenus as an array
but the component expects a record/map; update mockShellState.expandedMenus to
match the real contract (e.g., an object/Record<string, boolean> or empty {}),
so tests reflect the actual shape consumed by AppLayout and any functions
referencing expandedMenus (search for mockShellState and expandedMenus in the
test file to change it).

In `@apps/customer-portal/webapp/src/pages/ProjectHub.tsx`:
- Around line 39-96: The infinite-query returned by useGetProjects({}, true) is
not being drained, so only the first page is rendered; call the query's
pagination helpers (e.g., fetchNextPage while hasNextPage is true) to auto-fetch
remaining pages and append them to projectsResponse.pages. Add logic in a
useEffect that depends on isLoading, hasNextPage, and fetchNextPage (or
implement a helper like drainAllPages) to sequentially or concurrently
fetchNextPage until hasNextPage is false, then rely on
projectsResponse?.pages.flatMap(...) to render the full list. Ensure you
reference the existing variables fetchNextPage and hasNextPage from the
useGetProjects hook and guard against repeated calls using isLoading or a local
draining flag.

In `@apps/customer-portal/webapp/src/pages/ProjectPage.tsx`:
- Around line 37-52: ProjectPage currently reads projectId from useParams
without guarding against undefined; update the component (ProjectPage) to handle
a missing projectId by providing a safe fallback or early return: check the
value returned from useParams({ projectId }) and if undefined render a sensible
fallback UI (e.g., "Unknown project" message, skeleton/loader, or redirect)
instead of rendering <strong>{projectId}</strong>, ensuring all usages in the
JSX use the guarded value.

In `@apps/customer-portal/webapp/src/utils/projectCard.ts`:
- Around line 24-42: formatProjectDate uses local-time Date and getDate(),
causing timezone shifts; update it to parse only the date portion (e.g., extract
the YYYY-MM-DD from the input) and construct a UTC-stable date or use Date.UTC,
then format using getUTCDate(), getUTCMonth()/toLocaleString with UTC options or
map month names manually. Ensure the same fallbacks remain (return "" for empty
input and original string on parse error) and keep the exported function name
formatProjectDate unchanged.
🧹 Nitpick comments (8)
apps/customer-portal/webapp/src/components/common/header/SearchBar.tsx (1)

26-35: Remove unnecessary React fragment.

The fragment wrapper is redundant since only a single element is returned.

♻️ Suggested simplification
 export default function SearchBar(): JSX.Element {
   return (
-    <>
-      {/* header search bar */}
-      <SearchBarUI
-        size="small"
-        placeholder="Search cases, tickets, or users"
-        sx={{ minWidth: 400 }}
-      />
-    </>
+    <SearchBarUI
+      size="small"
+      placeholder="Search cases, tickets, or users"
+      sx={{ minWidth: 400 }}
+    />
   );
 }
apps/customer-portal/webapp/src/models/mockFunctions.ts (2)

30-34: Non-deterministic mock functions may cause flaky tests.

Using Math.random() without seeding produces different values each run, which can lead to non-reproducible test failures. The JSDoc comments describe these as "deterministic random" but they are not deterministic.

For test stability, consider either:

  1. Accepting a seed parameter for reproducibility
  2. Returning fixed values for unit tests
  3. Using the existing mock arrays consistently (as done for the first three functions)

63-69: Inconsistent approach compared to other mock functions.

getMockProjectSupportStats uses inline Math.random() * N ranges while the other three functions sample from predefined arrays (mockActiveChatsOptions, etc.). Consider using predefined arrays for consistency, or document why different approaches are used.

♻️ Consistent approach using predefined sampling
+const mockTotalCasesOptions: number[] = [0, 10, 25, 50];
+const mockResolvedChatsOptions: number[] = [0, 5, 15, 30];
+const mockSessionChatsOptions: number[] = [0, 3, 10, 20];

 export const getMockProjectSupportStats = (): ProjectSupportStats => {
   return {
-    activeChats: Math.floor(Math.random() * 10),
-    resolvedChats: Math.floor(Math.random() * 30),
-    sessionChats: Math.floor(Math.random() * 20),
-    totalCases: Math.floor(Math.random() * 50),
+    activeChats: getMockActiveChats(),
+    resolvedChats: mockResolvedChatsOptions[Math.floor(Math.random() * mockResolvedChatsOptions.length)],
+    sessionChats: mockSessionChatsOptions[Math.floor(Math.random() * mockSessionChatsOptions.length)],
+    totalCases: mockTotalCasesOptions[Math.floor(Math.random() * mockTotalCasesOptions.length)],
   };
 };
apps/customer-portal/webapp/src/context/logger/LoggerContext.tsx (1)

22-29: Incorrect JSDoc @returns usage for a constant.

The @returns tag is intended for documenting function return values, not constants. LoggerContext is a constant assigned via createContext(), so @returns is semantically incorrect here. The type is already explicitly declared on line 27.

📝 Suggested fix
 /**
  * Context object for the Logger service.
- *
- * `@returns` {Context<ILogger | null>} The LoggerContext.
  */
 const LoggerContext: Context<ILogger | null> = createContext<ILogger | null>(
apps/customer-portal/webapp/src/components/common/header/UserProfile.tsx (1)

17-47: Verify router import and mock-user wiring before release.
Line 19 imports useNavigate from react-router, which is uncommon in web apps that use react-router-dom/BrowserRouter; please confirm this is intentional and works with your router setup. Also confirm mockUser (Line 43) is only a temporary placeholder and will be replaced with real user context before release.

apps/customer-portal/webapp/src/components/support/CasesOverviewStats.tsx (1)

55-95: Avoid double-casting Skeleton into StatCard.value.
Line 86 forces a Skeleton into a number via as unknown as number, which bypasses type safety and may break if StatCard formats/assumes numeric values. Please verify StatCard supports ReactNode values; otherwise render a dedicated loading branch or update the component API accordingly. (Also consider using stat.key as the key instead of the array index to avoid reorder issues.)

apps/customer-portal/webapp/src/api/__tests__/useGetProjectSupportStats.test.tsx (1)

17-88: Consider fake timers to avoid the real 800 ms delay.
These tests currently wait for the hook’s real timeout, which can slow down the suite. Using Vitest fake timers would keep the tests fast and deterministic.

♻️ Possible test refactor
 import { beforeEach, describe, expect, it, vi } from "vitest";
@@
   beforeEach(() => {
+    vi.useFakeTimers();
     queryClient = new QueryClient({
@@
     mockLogger.debug.mockClear();
     mockLogger.error.mockClear();
   });
+  afterEach(() => {
+    vi.useRealTimers();
+  });
@@
   it("should return data after fetching", async () => {
@@
-    await waitFor(() => expect(result.current.isSuccess).toBe(true));
+    await vi.runAllTimersAsync();
+    await waitFor(() => expect(result.current.isSuccess).toBe(true));
apps/customer-portal/webapp/src/components/common/sideNavBar/SideBar.tsx (1)

64-65: Derive the active item via route matching for nested paths.

Using pathname.split("/").pop() breaks highlighting when nested routes (e.g., /support/cases) or query fragments are added. Consider matching against APP_SHELL_NAV_ITEMS with matchPath so the active menu stays stable.

Suggested update
-import { useLocation, useParams, Link as NavigateLink } from "react-router";
+import {
+  useLocation,
+  useParams,
+  Link as NavigateLink,
+  matchPath,
+} from "react-router";

-const activeItem = location.pathname.split("/").pop() || "dashboard";
+const activeItem =
+  APP_SHELL_NAV_ITEMS.find((item) =>
+    matchPath(`/:projectId/${item.path}/*`, location.pathname),
+  )?.id ??
+  (matchPath(`/:projectId/settings`, location.pathname) ? "settings" : "dashboard");

Comment thread apps/customer-portal/webapp/src/api/useGetProjects.ts
Comment thread apps/customer-portal/webapp/src/pages/ProjectHub.tsx
Comment thread apps/customer-portal/webapp/src/pages/ProjectPage.tsx
Comment thread apps/customer-portal/webapp/src/utils/projectCard.ts
Call fetchNextPage in a useEffect to drain all pages when useGetProjects indicates more pages are available. Adds hasNextPage, isFetchingNextPage and fetchNextPage to the hook destructuring and guards the auto-fetch so it only runs when not currently fetching and there is no error. Also adds a unit test to verify fetchNextPage is invoked when hasNextPage is true.
Change mockShellState.expandedMenus from an array to an object in apps/customer-portal/webapp/src/layouts/__tests__/AppLayout.test.tsx to match the expected shell state shape and avoid type inconsistencies in tests.
Stop calling getMock* functions in the component signature and import useMemo to memoize mock values inside the component. Compute resolvedStatus/resolvedOpenCases/resolvedActiveChats with nullish coalescing and pass those resolved values to ProjectCardBadges and ProjectCardStats. This ensures mock defaults aren't regenerated on every render and preserves optional prop behavior.
dileepapeiris and others added 2 commits February 2, 2026 15:01
Update the warning message in Header.tsx to use uppercase 'ID' instead of 'id' for clarity and consistency when a project is not found during switching.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 `@apps/customer-portal/webapp/src/components/common/header/Header.tsx`:
- Around line 69-88: The Project fetch can fail and currently Header.tsx passes
only isLoading and projects to ProjectSwitcher, causing an empty dropdown on
error; update the Header component to surface errors by passing isError (and
optionally error object from useGetProjects) into <ProjectSwitcher> and update
the auto-pagination useEffect to avoid calling fetchNextPage when isError is
true (keep the current guard) or render an inline error message in Header when
isError is true; specifically modify the hook usage (useGetProjects) destructure
error/isError and pass isError (and error) into ProjectSwitcher, or render an
error UI in Header before ProjectSwitcher when isError is true so users see an
error state instead of an empty list.
- Around line 164-191: The Header currently hardcodes HeaderUI.Toggle
collapsed={false}; update the Header component to accept the real sidebar
collapsed state from its parent (prop name e.g. sidebarCollapsed or collapsed)
and pass that value into HeaderUI.Toggle instead of false; modify the Header
function signature to include the new prop, wire it where Header is instantiated
(AppLayout/shellState.sidebarCollapsed) and ensure the existing onToggleSidebar
handler is still passed to HeaderUI.Toggle so the toggle icon reflects the
actual sidebarCollapsed state.
🧹 Nitpick comments (1)
apps/customer-portal/webapp/src/components/common/header/Header.tsx (1)

93-96: Consider removing explicit useMemo since React Compiler handles memoization.

The customer-portal webapp has React Compiler enabled, which automatically optimizes re-renders. This useMemo is not harmful but is redundant.

Based on learnings: "The customer-portal webapp has React Compiler (babel-plugin-react-compiler v1.0.0) enabled via the Vite React plugin configuration. Manual memoization using useCallback, useMemo, or React.memo is unnecessary as the compiler handles these optimizations automatically."

♻️ Optional: Remove manual memoization
-  const projects = useMemo(
-    () => projectsResponse?.pages.flatMap((page) => page.projects) || [],
-    [projectsResponse?.pages],
-  );
+  const projects =
+    projectsResponse?.pages.flatMap((page) => page.projects) || [];

Comment thread apps/customer-portal/webapp/src/components/common/header/Header.tsx
Comment thread apps/customer-portal/webapp/src/components/common/header/Header.tsx
@dileepapeiris
dileepapeiris marked this pull request as draft February 4, 2026 03:01
@dileepapeiris
dileepapeiris marked this pull request as ready for review February 4, 2026 03:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

App/Customer Portal Area/Frontend Platform/Web Type/Improvement Marks enhancements or improvements to existing features Type/UX Refers to user experience-related tasks or issues

Projects

Status: Staging Deployed

Development

Successfully merging this pull request may close these issues.

3 participants