Skip to content

[Customer Portal][FE][Web] Implement Project Card Suite, Project Hub Page, and Loading Skeletons - #76

Merged
Rashmika998 merged 48 commits into
wso2-open-operations:customer-portal-milestone-1from
dileepapeiris:feat/add-project-cards
Feb 3, 2026
Merged

Rashmika998 merged 48 commits into
wso2-open-operations:customer-portal-milestone-1from
dileepapeiris:feat/add-project-cards

Conversation

@dileepapeiris

@dileepapeiris dileepapeiris commented Jan 31, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

This PR introduces the Project Card Suite and the functional Project Hub page. It focuses on providing a rich, interactive interface for project discovery and navigation, ensuring users have a seamless experience when viewing and selecting their projects.

ScreenRecordings and screenshots

ProjectHub Page

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

**Light mode: Medium Size Screen **
image

**Dark mode: Medium Size Screen **
image

**Light mode: Large Size Screen **
image

**Dark mode: Large Size Screen **
image

ProjectCard Loading State

**Light mode **
image

**Dark mode **
image

**Animations **

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

Goals

  • Implement a modular and reusable ProjectCard component system including sub-components for badges, stats, and actions.
  • Build the ProjectHub page to handle paginated project lists with full lifecycle states (loading, error, empty).
  • Enhance the UI experience with high-fidelity loading skeletons for project cards.
  • Centralize project-related business logic into utility functions and typed models.

Approach

  • Modular Component Architecture: Broke down the ProjectCard into smaller, focused sub-components (ProjectCardBadge, ProjectCardStats, etc.) to improve maintainability and readability.
  • State Management: The ProjectHub utilizes the useSearchProjects hook to manage infinite scrolling/pagination and gracefully handles edge cases like "No results found."
  • Visual Consistency: Created utility functions (getStatusColor) to map backend status strings to design-system-compliant colors consistently across the app.
  • Developer Experience: Expanded mock data and helper functions to facilitate easier testing and local development without a live backend.

User stories

  • Project Overview: As a user, I can see a summary of my projects, including their current status and key statistics.
  • Seamless Navigation: As a user, I can click on a project card to be navigated directly to that specific project's dashboard.
  • Visual Feedback: As a user, I see skeleton loaders while my projects are being fetched, providing a smoother perceived performance.
  • Data Clarity: As a user, I can see formatted dates and color-coded status badges that make project information easy to scan.

Walkthrough

This PR adds the core visual elements for project management: a comprehensive suite of Project Card components (including skeletons), the main Project Hub page with pagination logic, standardized models/mock data for API consistency, and utility functions for date and status formatting.

Changes

Cohort File(s) Summary
Project Card Suite ProjectCard.tsx, Badges.tsx, Info.tsx, Stats.tsx, Actions.tsx, Skeleton.tsx Complete implementation of the project card UI, including navigation behavior and loading states.
Pages ProjectHub.tsx, ProjectPage.tsx Implemented ProjectHub (paginated list with loading/error/empty states) and ProjectPage (project-specific template).
Models & Mock Data requests.ts, responses.ts, mockData.ts, mockFunctions.ts Defined API interfaces and extended mock data sets for robust development/testing.
Utilities projectCard.ts, __tests__/projectCard.test.ts Added formatProjectDate and getStatusColor utilities with 100% test coverage.

Sequence Diagram

sequenceDiagram
    actor User
    participant BrowserRouter as BrowserRouter
    participant App as App
    participant AppLayout as AppLayout
    participant Header as Header
    participant ProjectHub as ProjectHub
    participant useSearch as useSearchProjects
    participant ProjectCard as ProjectCard

    User->>BrowserRouter: Request "/"
    BrowserRouter->>App: Route match
    App->>AppLayout: Render layout (Outlet)
    AppLayout->>Header: Render header
    Header->>Header: Determine hub route ("/")
    AppLayout->>ProjectHub: Render ProjectHub
    ProjectHub->>useSearch: Fetch projects (offset=0, limit=10)
    useSearch-->>ProjectHub: Return paginated projects
    ProjectHub->>ProjectCard: Render project cards
    User->>ProjectCard: Click card
    ProjectCard->>BrowserRouter: Navigate to "/:projectId/dashboard"
    BrowserRouter->>AppLayout: Re-render with project route
    AppLayout->>Header: Header updates (show ProjectSwitcher)
    Header->>useSearch: Ensure projects loaded for switcher
    User->>Header: Select different project
    Header->>BrowserRouter: Navigate to new "/:projectId/<subpath>"
Loading

Automation tests

  • Component Testing: Unit tests for ProjectCard and all sub-components to ensure correct prop rendering and navigation triggers.
  • Utility Testing: Exhaustive tests for formatProjectDate to handle various locales and getStatusColor for all project status variants.
  • Page Testing: Integration tests for ProjectHub to verify the transition between Loading, Data, and Empty states.
image

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a project hub for browsing and discovering projects with detailed cards displaying status, active chats, and open cases.
    • Added project switching capability in the header for quick navigation between projects.
    • Added header actions including a theme toggle, community join button, and user profile menu.
    • Added navigation sidebar for accessing dashboard, settings, and other project sections.
    • Added infinite scrolling support for improved project discovery at scale.
  • Tests

    • Added comprehensive test coverage for all new UI components and utilities.

Rashmika998 and others added 30 commits January 13, 2026 10:34
Introduce a new ProjectCard React component (apps/customer-portal/webapp/src/components/projectCard/ProjectCard.tsx). The file defines typed props, uses default mock values for status/openCases/activeChats, and handles navigation via useNavigate or an onViewDashboard callback. It renders a Form.CardButton containing ProjectCardBadges, ProjectCardInfo, ProjectCardStats, and ProjectCardActions. Includes Apache 2.0 license header.
Introduce a new ProjectCardActions React component (apps/customer-portal/webapp/src/components/projectCard/ProjectCardActions.tsx) that renders the action area for a project card. It uses @wso2/oxygen-ui Form.CardActions, Stack and Button with an ArrowRight icon to display a full-width "View Dashboard" outlined secondary button and applies spacing/styling; file includes the project license header.
Introduce a new utils file for project cards that provides formatProjectDate to render dates as "Created DD MMM YYYY" (gracefully handles empty/invalid inputs) and getStatusColor to map status strings to theme colors (All Good → success, Need Attention → warning, Critical Issues → error, fallback → default). Includes license header and basic validation.
Add Vitest unit tests for utils/projectCard: formatProjectDate and getStatusColor. Tests cover multiple date formats (YYYY-MM-DD, ISO), empty input, and invalid dates (expect original string), and verify status-to-color mapping ('All Good' -> success, 'Need Attention' -> warning, 'Critical Issues' -> error, unknown -> default).
Rename the project identifier from `key` to `id` across the ProjectSwitcher component. The prop `onProjectChange` now receives a `projectId`, the select's value uses `selectedProject?.id`, project lookup uses `project.id`, and ComplexSelect.MenuItem keys/values are set to `project.id`. This standardizes the component to use the project's id field for selection and callbacks.
Update Actions.tsx to import UserProfile via the project alias (@/components/header/UserProfile) instead of the relative path. This standardizes import paths and avoids brittle relative imports.
Update Header component to use project.id rather than project.key for lookups, logging and navigation. Also switch local component imports to absolute paths (@/components/header/...). Adjusted handleProjectChange parameter and related messages, and updated route navigation to use the project id for switching.
Replace the inline type assertion with an explicit SearchProjectsResponse annotation for the `results` constant in useSearchProjects.ts. This clarifies the expected shape of the response and removes the redundant `as` cast; no runtime behavior changes.
Replace the relative import with the project path alias for useSearchProjects in the test file. This aligns the test with the codebase's absolute import conventions and helps ensure consistent module resolution in the test environment. Affected file: apps/customer-portal/webapp/src/api/__tests__/useSearchProjects.test.tsx.
Update Brand.test.tsx to import Brand using the project path alias ("@/components/header/Brand") instead of a relative import. This aligns the test with the app's module resolution and avoids issues with relative paths in the test environment.
Update Header tests to use project.id instead of project.key. The mocked ProjectSwitcher option value and the fireEvent change/expected navigate path were adjusted to use mockProjects[*].id (e.g. `/${id}/dashboard`) so the tests match the component's current behavior.
Introduce ProjectCardSkeleton in apps/customer-portal/webapp/src/components/projectCard to provide a loading placeholder for ProjectCard. The component (default export) uses @wso2/oxygen-ui primitives (Card, Form, Skeleton, Typography, Box, Stack, Divider) to render skeletons for badges, header/subheader, stats (open cases, active chats, date) and actions. File includes WSO2 Apache-2.0 license header and applies simple flexbox-based styling for consistent sizing and spacing.
Introduce ProjectCardStats.tsx: a new component that renders the stats section for a project card (open cases, active chats, and formatted date). It defines a ProjectCardStatsProps interface, uses @wso2/oxygen-ui components and icons, and formats the date via formatProjectDate. Includes layout and styling (Typography, Box, Divider) and exports the component as the default. File includes Apache 2.0 license header.
Introduce a new ProjectCardBadges React component (TypeScript) to render the top badges for a project card. The component accepts projectKey and status props, displays two Chips inside a Form.CardContent layout, and uses getStatusColor(status) to determine the status chip color. File includes the project license header and imports from @wso2/oxygen-ui.
Introduce ProjectCardInfo React component under apps/customer-portal/webapp/src/components/projectCard. The component accepts title and subtitle props (ProjectCardInfoProps) and renders them using Form.CardHeader and Typography from @wso2/oxygen-ui. The subtitle is styled for multi-line truncation (WebkitLineClamp: 4, wordBreak, overflow hidden) with a minimum height to preserve layout. File includes Apache-2.0 license header.
Replace placeholder project entries with realistic WSO2 project data (ids, keys, names, descriptions, createdOn). Add mockStatusOptions, mockOpenCasesOptions, and mockActiveChatsOptions arrays for testing UI/status filters and counts. Update mockUser to a new sample user (name, email, avatar). These changes provide more representative sample data for the customer portal webapp during development and testing.
Import Box from @wso2/oxygen-ui and wrap the <Outlet /> in a Box with sx={{ p: 3, flex: 1, overflow: 'auto' }} inside AppShell.Main. This adds consistent padding, makes the main area flexible, and enables internal scrolling for page content.
Replace the relative './SubscriptionWidget' import with the project alias '@/components/sideNavBar/SubscriptionWidget' for consistent absolute imports and module resolution. No functional changes.
Update AppLayout.test.tsx to import AppLayout via the path alias (@/layouts/AppLayout) and add a simple Box mock to the @wso2/oxygen-ui module mock. The Box mock provides a basic wrapper for children to avoid rendering issues during tests.
Replace the relative import with the project path alias for SubscriptionWidget in the sideNavBar test. This aligns the test with the project's module path mappings and avoids fragile relative paths when files move.
Introduce apps/customer-portal/webapp/src/models/mockFunctions.ts which exports three helpers: getMockStatus, getMockOpenCases, and getMockActiveChats. Each function returns a random value drawn from mockStatusOptions, mockOpenCasesOptions, and mockActiveChatsOptions respectively (imported from @/models/mockData). These utilities centralize generation of random mock values for UI components and testing within the customer portal.
Replace the old project list buttons with a responsive flex grid of ProjectCard components and add ProjectCardSkeleton placeholders during loading. Removed useNavigate and the previous Button click layout; projects are now wrapped in responsive Box containers with breakpoints for different screen sizes. Added FolderOpen icon and updated page title/subtitle and overall layout structure to center content and improve visual presentation.
Introduce a new test suite for the ProjectHub page (apps/customer-portal/webapp/src/pages/__tests__/ProjectHub.test.tsx). Uses Vitest and React Testing Library and adds mocks for @wso2/oxygen-ui, icons, ProjectCard components, useSearchProjects hook, and useLogger. Covers loading skeletons, successful project rendering, empty state, and error handling (including logger assertions) to validate UI and logging behavior.
Add unit tests for the Logger utility using Vitest. Tests cover log level filtering (DEBUG/INFO/WARN/ERROR), message formatting (timestamp, level, prefix), passing additional arguments to console methods, and parseLogLevel behavior. Console methods are spied/mocked and fake timers with a fixed system time are used to make timestamp assertions deterministic.
Update SideBar.test.tsx to import SideBar via the project alias ('@/components/sideNavBar/SideBar') instead of a relative path. This aligns the test with the application's path aliasing and reduces brittle relative imports; no functional changes to the test logic.
Add unit tests for ProjectCardStats using vitest and React Testing Library. The new test file mocks @wso2/oxygen-ui, icon package, and the projectCard util (formatProjectDate), and verifies that counts, formatted date, and icons render correctly.
Add clarifying block comments in useSearchProjects.ts describing the stable query key and mock network delay behavior. Update the AppLayout test mock for SideBar to match the component signature (remove expandedMenus from the mocked props) to prevent a prop mismatch in tests.
Update Header.test.tsx to improve project switcher and search projects mocks and add coverage for pagination/error handling. Introduce mockFetchNextPage and mockUseSearchProjects, change the useSearchProjects mock to delegate to mockUseSearchProjects, and set up a default return value in beforeEach. Revise the ProjectSwitcher mock to accept projects and selectedProject props (including data-selected-id and option value handling). Add tests to verify clearing selection for invalid/missing projectId and to assert fetchNextPage is called when hasNextPage is true and not called when isError is true.
Import beforeEach from vitest and add a beforeEach hook that calls mockOnProjectChange.mockClear(). This ensures the mock is reset between tests to avoid cross-test interference.
Stop requesting additional project pages when the search hook has errored by adding isError to the destructure, gating fetchNextPage with !isError, and adding isError to the effect dependencies. Improve project selection logic by renaming the find callback variable to avoid shadowing, comparing projects by id (not key), clearing the selected project when no matching project is found, and clearing selection when projectId is absent (e.g., on the project hub).
Replace the previous css: { include: [] } setting with css: true in apps/customer-portal/webapp/vite.config.ts. This enables CSS handling in the Vitest environment (allowing CSS imports during tests) and simplifies the test config.
Add RTL waitFor to ProjectHub tests and make relevant test cases async to reliably assert mocked logger calls. Imported waitFor from @testing-library/react and wrapped mockLogger.error/debug expectations in waitFor to avoid flaky failures from async side-effects when rendering ProjectHub.
@dileepapeiris dileepapeiris changed the title Feat/add project cards [Customer Portal][FE][Web] Implement Project Card Suite, Project Hub Page, and Loading Skeletons Jan 31, 2026
@dileepapeiris

Copy link
Copy Markdown
Contributor Author

Note for reviewers:
Before merging this, please merge
#73

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

@Rashmika998 Rashmika998 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.

LGTM

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

Labels

Projects

Status: Staging Deployed

Development

Successfully merging this pull request may close these issues.

4 participants