[Customer Portal][FE][Web] Implement Outstanding Cases component on Support page - #101
Conversation
Add a new support utilities file to the customer-portal webapp. Exposes resolveColorFromTheme(path, theme) which resolves a color from theme.palette using a dot-separated path (falls back to the input path if not found), and formatRelativeTime(date) which formats a Date or date string into human-readable relative times ("just now", "X minutes/hours/days/months/years ago") and returns "--" for invalid or undefined dates.
Correct inverted color mappings in getStatusColor: 'open' now returns info.main (was primary.main) and 'awaiting' now returns primary.main (was info.main). Aligns status color outputs with the intended design/spec.
Introduce OutstandingCasesList, a new React component to render outstanding support case rows for the customer portal overview card. Handles loading via OutstandingCasesSkeleton and shows an empty state message. Uses theme-aware colors (getStatusColor/getPriorityColor + resolveColorFromTheme), formats relative times, displays assigned engineer tooltip, and composes UI with @wso2/oxygen-ui Form/Card components and Chip.
Add unit tests for OutstandingCasesList (apps/customer-portal/webapp/src/components/support/support-overview-cards/__tests__/OutstandingCasesList.test.tsx). Tests use vitest and @testing-library/react and cover empty state rendering and rendering of case details (case number, title, severity, status, assigned engineer and relative created time) using a mocked CaseListItem.
Add unit tests for the SupportOverviewCard component using Vitest and React Testing Library. Tests verify rendering of title, subtitle, icon, children and footer button, and assert that the onFooterClick handler is called when the footer button is clicked.
Introduce a new reusable SupportOverviewCard React component (TypeScript) for support overview sections such as Outstanding Cases and Chat History. The component accepts title, subtitle, icon, children, footer CTA label and handler, and supports an iconVariant (orange|blue) to drive palette and icon color. Built with WSO2 Oxygen UI primitives and theme-aware styling, it renders a header with icon, content area for children, and a full-width footer button with an arrow icon.
Introduce OutstandingCasesSkeleton.tsx to provide a loading state for the outstanding cases list. The new component renders five placeholder cards using @wso2/oxygen-ui Box, Form.CardButton and Skeleton variants (text, circular, rounded) and applies layout styles via sx. Includes Apache 2.0 license header.
Introduce SUPPORT_OVERVIEW_CASES_LIMIT (value 5) in supportConstants.ts to control how many outstanding cases are shown on support overview cards. Provides a single source of truth for the UI limit and documents intent with an inline comment.
Fetch and display recent project cases on the Support page. Added useNavigate and useGetProjectCases with SUPPORT_OVERVIEW_CASES_LIMIT to load latest cases (sorted by createdOn). Introduced a SupportOverviewCard inside a Grid, showing an "Outstanding Cases" section with FileText icon and an OutstandingCasesList child; footer button navigates to the dashboard. Also wired isCasesLoading into the UI and preserved existing support stats and chat banner.
📝 WalkthroughWalkthroughAdds new React components (OutstandingCasesList, OutstandingCasesSkeleton, SupportOverviewCard) to the customer portal's support overview section, including utility functions for time formatting and theme color resolution. Integrates case data fetching and display into SupportPage with loading states and minor color mapping adjustments. Changes
Sequence DiagramsequenceDiagram
participant SupportPage
participant useGetProjectCases
participant OutstandingCasesList
participant OutstandingCasesSkeleton
SupportPage->>useGetProjectCases: fetch cases (limit: 5, sort: createdOn desc)
useGetProjectCases-->>SupportPage: return cases data + isLoading state
alt isLoading = true
SupportPage->>OutstandingCasesSkeleton: render skeleton cards
OutstandingCasesSkeleton-->>SupportPage: display loading placeholders
else cases exist
SupportPage->>OutstandingCasesList: pass cases + loading state
OutstandingCasesList->>OutstandingCasesList: render case cards with details
OutstandingCasesList-->>SupportPage: display case list
else no cases
OutstandingCasesList->>OutstandingCasesList: render empty state message
OutstandingCasesList-->>SupportPage: display "No outstanding cases."
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 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: 2
🤖 Fix all issues with AI agents
In
`@apps/customer-portal/webapp/src/components/support/support-overview-cards/OutstandingCasesList.tsx`:
- Around line 108-119: The Typography that renders c.title in
OutstandingCasesList currently uses overflow: "hidden" and textOverflow:
"ellipsis" but omits whiteSpace, so the ellipsis never appears; update the sx
object on that Typography (inside Form.CardContent) to include whiteSpace:
"nowrap" so textOverflow: "ellipsis" works (optionally ensure the container has
a constrained width if needed).
In `@apps/customer-portal/webapp/src/utils/support.ts`:
- Around line 24-33: The file references the Theme type in resolveColorFromTheme
but never imports it; add a type import for Theme at the top of the file (after
the license header) from your UI library (the same module other files use for
Theme, e.g. import { Theme } from '@mui/material/styles') so
resolveColorFromTheme(path: string, theme: Theme) compiles.
🧹 Nitpick comments (10)
apps/customer-portal/webapp/src/utils/support.ts (1)
41-73:formatRelativeTimelooks correct overall; note the approximate month/year buckets.The 30-day-month approximation is standard for relative-time displays and acceptable here. Edge-case: negative
diffInSeconds(future dates) will fall through all guards and display a negative year/month count (e.g., "-1 years ago"). Consider returning a fallback for future dates if that's a realistic input.apps/customer-portal/webapp/src/components/support/support-overview-cards/OutstandingCasesSkeleton.tsx (1)
28-28: Replace magic number5withSUPPORT_OVERVIEW_CASES_LIMIT.This value duplicates the constant defined in
supportConstants.ts. Using the constant ensures both stay in sync.Proposed fix
+import { SUPPORT_OVERVIEW_CASES_LIMIT } from "@constants/supportConstants";- {Array.from({ length: 5 }).map((_, index) => ( + {Array.from({ length: SUPPORT_OVERVIEW_CASES_LIMIT }).map((_, index) => (apps/customer-portal/webapp/src/components/support/support-overview-cards/__tests__/OutstandingCasesList.test.tsx (1)
39-53: Consider adding a test for the loading state (isLoading={true}).The component renders
OutstandingCasesSkeletonwhenisLoadingis true, but this path isn't tested. A quick test asserting skeleton presence would improve coverage of the component's three states (loading, empty, populated).apps/customer-portal/webapp/src/api/__tests__/useGetChatHistory.test.tsx (2)
140-170:window.configrestoration is not failure-safe.If the test throws before line 169,
window.configwon't be restored, potentially polluting subsequent tests. Consider usingafterEachor wrapping intry/finally.♻️ Suggested improvement
it("should fetch real data when mock is disabled", async () => { mockIsMockEnabled = false; const mockResponse = { /* ... */ }; const originalConfig = window.config; - window.config = { - CUSTOMER_PORTAL_BACKEND_BASE_URL: "https://api.test", - } as typeof window.config; - - vi.stubGlobal( - "fetch", - vi.fn().mockResolvedValue({ - ok: true, - json: () => Promise.resolve(mockResponse), - status: 200, - } as Response), - ); - - const { result } = renderHook( - () => useGetChatHistory("project-1"), - { wrapper }, - ); - - await waitFor(() => expect(result.current.isSuccess).toBe(true)); - - expect(result.current.data).toEqual(mockResponse); - expect(mockGetIdToken).toHaveBeenCalled(); - expect(mockLogger.debug).toHaveBeenCalledWith( - expect.stringContaining( - "Fetching chat history for project ID: project-1, mock: false", - ), - ); - - window.config = originalConfig; + try { + window.config = { + CUSTOMER_PORTAL_BACKEND_BASE_URL: "https://api.test", + } as typeof window.config; + + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve(mockResponse), + status: 200, + } as Response), + ); + + const { result } = renderHook( + () => useGetChatHistory("project-1"), + { wrapper }, + ); + + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(result.current.data).toEqual(mockResponse); + expect(mockGetIdToken).toHaveBeenCalled(); + } finally { + window.config = originalConfig; + } });The same applies to the error-handling test at lines 172–201.
66-69: Redundant manual mock clearing.Lines 66–67 manually clear
mockLogger.debugandmockLogger.error, butvi.clearAllMocks()on line 69 already clears all mocks. The manual calls are unnecessary.apps/customer-portal/webapp/src/pages/SupportPage.tsx (1)
20-20:MessageSquareis imported but unused.
MessageSquareis imported on line 20 but not referenced anywhere in the JSX. If it's intended for a future chat history card, consider deferring the import until it's needed.apps/customer-portal/webapp/src/components/support/support-overview-cards/SupportOverviewCard.tsx (4)
106-119: Footer button color is hardcoded to"warning"regardless oficonVariant.This component is documented as a generic card for multiple sections (Outstanding Cases, Chat History). When used with
iconVariant="blue", the header icon gets blue/info styling, but the footer CTA remains orange/warning—visually inconsistent. Consider deriving the button color from the variant.♻️ Suggested fix
<Button fullWidth variant="text" - color="warning" + color={paletteKey} onClick={onFooterClick} endIcon={<ArrowRight size={16} />}
98-105: Consider using theDividercomponent instead of an emptyBoxwith a border.An empty
Boxsolely for a horizontal rule is less semantic. Oxygen UI'sDividerconveys the same intent more clearly and keeps the markup consistent with the design system.♻️ Suggested fix
Add
Dividerto the existing import:import { Box, Button, + Divider, Paper, Typography, alpha, colors, useTheme, } from "@wso2/oxygen-ui";Replace the empty Box:
- <Box - sx={{ - borderTop: 1, - borderColor: "divider", - pt: 2, - mt: 1, - }} - /> + <Divider sx={{ mt: 1 }} />
55-58: Icon color bypasses the theme while the background references it.
iconColoruses hardcodedcolors.blue[600]/colors.orange[600], but the chip background (line 74) derives fromtheme.palette[paletteKey]. In a custom or dark theme, these could diverge. For consistency, consider pulling the icon color from the theme palette as well (e.g.,theme.palette[paletteKey].mainor.dark).
69-92: NestedPaperfor the icon chip may render with unintended elevation/shadow.The inner
Paper(line 70) used as the icon background container inherits the default Paper elevation. If a flat look is intended, explicitly setelevation={0}to avoid a subtle shadow.♻️ Suggested fix
<Paper + elevation={0} sx={{ width: 40, height: 40,
There was a problem hiding this comment.
Pull request overview
Implements a new “Outstanding Cases” overview card on the Support page (Customer Portal webapp) and introduces supporting UI utilities and data-fetching/mocking for related support features (including chat history).
Changes:
- Add
SupportOverviewCard+OutstandingCasesList(with skeleton + tests) and render it onSupportPagewith a limited, sorted cases fetch. - Introduce shared support utils for theme color resolution and relative time formatting.
- Add chat history response models, mock data/functions, and a
useGetChatHistoryReact Query hook with tests.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/customer-portal/webapp/src/utils/support.ts | New support utilities (theme color resolution, relative time formatting). |
| apps/customer-portal/webapp/src/utils/casesTable.ts | Adjust status-to-color mapping used by case UI. |
| apps/customer-portal/webapp/src/pages/SupportPage.tsx | Fetch latest cases and render Outstanding Cases overview card. |
| apps/customer-portal/webapp/src/models/responses.ts | Add chat history response typings. |
| apps/customer-portal/webapp/src/models/mockFunctions.ts | Add mock chat history accessor for API hook. |
| apps/customer-portal/webapp/src/models/mockData.ts | Add mock chat history dataset. |
| apps/customer-portal/webapp/src/constants/supportConstants.ts | Add overview cases limit constant. |
| apps/customer-portal/webapp/src/constants/apiConstants.ts | Add React Query key for chat history. |
| apps/customer-portal/webapp/src/components/support/support-overview-cards/SupportOverviewCard.tsx | New reusable overview card component. |
| apps/customer-portal/webapp/src/components/support/support-overview-cards/OutstandingCasesList.tsx | New Outstanding Cases list UI (status chip, severity dot, relative time). |
| apps/customer-portal/webapp/src/components/support/support-overview-cards/OutstandingCasesSkeleton.tsx | Loading skeleton for the Outstanding Cases list. |
| apps/customer-portal/webapp/src/components/support/support-overview-cards/tests/SupportOverviewCard.test.tsx | Unit tests for overview card component. |
| apps/customer-portal/webapp/src/components/support/support-overview-cards/tests/OutstandingCasesList.test.tsx | Unit tests for Outstanding Cases list component. |
| apps/customer-portal/webapp/src/api/useGetChatHistory.ts | New API hook for fetching (or mocking) chat history. |
| apps/customer-portal/webapp/src/api/tests/useGetChatHistory.test.tsx | Unit tests for useGetChatHistory. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Use SUPPORT_OVERVIEW_CASES_LIMIT for skeleton rendering and remove unnecessary cursor styles and overflow ellipsis in the outstanding cases components. Update the assigned engineer label to “Assigned to {name}” and import OutstandingCasesList in SupportPage. Adjust unit tests for getStatusColor to reflect swapped expectations for "Open" and "Awaiting Response". Add missing colors/SxProps/Theme imports in support utilities.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@apps/customer-portal/webapp/src/pages/SupportPage.tsx`:
- Around line 87-100: Handle fetch errors from useGetProjectCases by checking
the hook's isError (e.g., isCasesError) and surface it: log the error
(console.error or app logger) and render an error state inside the
SupportOverviewCard (e.g., replace OutstandingCasesList with an inline error
message or pass an error prop so OutstandingCasesList can show an error UI)
while preserving loading behavior for isCasesLoading; also replace the hardcoded
subtitle "Latest 5 support tickets" with a dynamic string using
SUPPORT_OVERVIEW_CASES_LIMIT (e.g., `Latest ${SUPPORT_OVERVIEW_CASES_LIMIT}
support tickets`) so the text stays in sync with the limit.
In `@apps/customer-portal/webapp/src/utils/support.ts`:
- Around line 42-74: formatRelativeTime currently returns negative "ago" strings
for future dates; add a guard after computing diffInSeconds in
formatRelativeTime to handle future inputs (date or string) by returning a safe
value (e.g., "just now" or a clear future-facing message) when diffInSeconds is
negative rather than proceeding to compute minutes/hours/days; update logic
around the diffInSeconds/diffInMinutes/diffInHours variables so future dates
never produce "-N ... ago".
🧹 Nitpick comments (2)
apps/customer-portal/webapp/src/pages/SupportPage.tsx (1)
20-20: Unused import:MessageSquare.
MessageSquareis imported but never referenced in this file. It's likely intended for a future "Chat History" card, but leaving it here adds noise.apps/customer-portal/webapp/src/utils/support.ts (1)
17-17: Unused imports:colorsandSxProps.Neither
colorsnorSxPropsis referenced anywhere in this file. They may have been left over from an earlier iteration.Proposed fix
-import { colors, type SxProps, type Theme } from "@wso2/oxygen-ui"; +import type { Theme } from "@wso2/oxygen-ui";
…tanding-cases-chat-history-v1
2198aed
into
wso2-open-operations:customer-portal-milestone-1
Overview
This PR enhances the Support dashboard by introducing the Outstanding Cases overview and building the foundation for chat history retrieval. It combines UI component development, API hook implementation (supporting both mock and real environments), and utility updates for status mapping and time formatting.
Key Changes
1. Outstanding Cases UI
SupportOverviewCard,OutstandingCasesList, andOutstandingCasesSkeleton.SupportPage, fetching a limited, sorted list of project cases.2. Chat History API & Infrastructure
useGetChatHistorywith dual-mode support (switches between mock data and real backend calls viaVITE_USE_MOCK_DATA).ChatHistoryItemandChatHistoryResponseinterfaces.3. Utilities & Theming
getStatusColormappings for "open" and "awaiting" statuses.resolveColorFromThemefor consistent styling andformatRelativeTimefor human-readable timestamps.4. Testing
useGetChatHistorycovering loading, error, mock, and authenticated fetch scenarios.Logic Flow (Chat History Fetching)
sequenceDiagram participant UI as SupportPage / Hook Consumer participant Hook as useGetChatHistory participant Auth as AuthProvider participant API as Backend API participant Logger as Logger UI->>Hook: call useGetChatHistory(projectId) alt Mock Mode Enabled Hook->>Logger: debug "using mock" Hook-->>UI: return mockChatHistory (after delay) else Real Backend Mode Hook->>Auth: request id token Auth-->>Hook: id token / auth state Hook->>API: GET /chat-history?projectId (Auth Header) alt Success API-->>Hook: 200 JSON Response Hook-->>UI: parsed ChatHistoryResponse else Error API-->>Hook: Error Response Hook->>Logger: Error logs Hook-->>UI: throw error end end🛠 File Summary
useGetChatHistory.ts,apiConstants.tsSupportOverviewCard.tsx,OutstandingCasesList.tsx,OutstandingCasesSkeleton.tsxSupportPage.tsxresponses.ts,mockData.ts,mockFunctions.tssupport.ts,casesTable.tsuseGetChatHistory.test.tsx,SupportOverviewCard.test.tsx,OutstandingCasesList.test.tsxSummary by CodeRabbit
Release Notes
New Features
Bug Fixes