Skip to content

[Customer Portal][FE][Web] Implement Case Details Page + Routing, Table Accessibility, and API Refinement - #138

Merged
v15a1 merged 45 commits into
wso2-open-operations:customer-portal-milestone-1from
dileepapeiris:feat/add-specific-case-detail-api
Feb 12, 2026
Merged

v15a1 merged 45 commits into
wso2-open-operations:customer-portal-milestone-1from
dileepapeiris:feat/add-specific-case-detail-api

Conversation

@dileepapeiris

@dileepapeiris dileepapeiris commented Feb 12, 2026 •

Copy link
Copy Markdown
Contributor

Description

This pull request introduces a new case details page and enables navigation to it from the cases table. It also improves the accessibility and test coverage of the cases table, and includes minor refactors and formatting improvements in API-related test files.

image image image

Case Details Navigation and Routing:

  • Added CaseDetailsPage and updated the routing in App.tsx to support navigating to individual case details under /support/cases/:caseId. [1] [2]
  • Updated CasesTable to pass an onCaseClick handler that navigates to the correct case details page when a case is clicked.

Cases Table Component Enhancements:

  • Modified CasesList to accept an optional onCaseClick prop, making case titles clickable and accessible (keyboard and screen reader support) when provided. [1] [2] [3]
  • Updated tests for CasesList to cover the new clickable behavior, verifying that onCaseClick is called when provided and not called otherwise.

API and Test Code Improvements:

  • Refactored API test files to improve readability by formatting type assertions and window config assignments across multiple lines. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12]
  • Minor code style and type improvements in API hooks and tests, including flattening the enabled condition in useGetCaseAttachments, and simplifying logging in useGetProductUpdateLevels. [1] [2]

Test Utilities and Mocks:

  • Improved the Typography mock in CasesList tests to support onClick and other props, enabling better event simulation.
  • Updated CasesTable test to mock useNavigate for navigation testing.

API Endpoint Correction:

  • Fixed the endpoint in useGetCaseDetails to remove the project ID from the URL, now fetching case details directly via /cases/:caseId.

Summary by CodeRabbit

  • New Features
    • Added case details page with comprehensive case information display
    • Cases are now clickable; clicking navigates to an individual case view
    • Case details include tabs for Activity, Details, Attachments, Calls, and Knowledge Base
    • Display of assigned support engineer and case status with action buttons for case management

Introduce a new path alias "@case-details" in apps/customer-portal/webapp/vite.config.ts that maps to ./src/components/support/case-details/header. This simplifies imports for case details header components and keeps import paths consistent across the codebase.
Reformat the renderHook call in apps/customer-portal/webapp/src/api/__tests__/useGetCaseAttachments.test.tsx to a single-line invocation with the wrapper option on the following line. This is a purely stylistic change and does not alter test behavior.
Adjust formatting in useGetProductUpdateLevels tests: expand the importOriginal type annotation to a multi-line type and break up the window.config type casts across multiple lines. These are non-functional, readability/linting-oriented changes.
Adjust formatting in useGetProductUpdatesStats.test.tsx for readability: expand single-line type casts and renderHook calls to multi-line, and reformat window.config typing. No functional changes — purely stylistic/refactor to improve clarity in the test file.
Import CaseDetailsPage and nest routes so support/cases now has an index for AllCasesPage and a dynamic route support/cases/:caseId for CaseDetailsPage. This enables viewing individual case details while keeping the list as the default cases route.
Combine the multi-line `enabled` property into a single line for readability. No functional change: the condition remains `!!caseId && (isMockEnabled || (isSignedIn && !isAuthLoading))`.
Add a Vitest + React Testing Library test for the CaseDetailsSkeleton component in the case-details header. The test renders the component within ThemeProvider (createTheme) and asserts that multiple MUI Skeleton elements and at least one MUI Divider are present, ensuring the skeleton layout renders as expected. (apps/customer-portal/webapp/src/components/support/case-details/header/__tests__/CaseDetailsSkeleton.test.tsx)
Add a new test suite for CaseDetailsContent header (apps/customer-portal/.../header/__tests__/CaseDetailsContent.test.tsx). Tests cover loading skeleton, rendering with data, Details tab placeholder, Back button behavior, Focus Mode toggle (hide/show header content), and presence of action buttons. Uses vitest and React Testing Library, and mocks support and casesTable utilities as well as ThemeProvider wrapping.
Reformat TypeScript annotations and window.config casts in apps/customer-portal/webapp/src/api/__tests__/useGetUpdatesStats.test.tsx. The importOriginal mock type and the window.config cast/assignment were split into multiline expressions for improved readability and to satisfy linting/formatting rules. No functional behavior was changed.
Expose an optional `onCaseClick` prop on AllCasesList and wire it to each case CardButton. When provided the callback will be invoked with the clicked `CaseListItem`; if omitted the behavior is unchanged. This allows parent components to handle case selection/click events.
Change the request URL in useGetCaseDetails to call /cases/{caseId} instead of /projects/{projectId}/cases/{caseId}. This removes the projectId segment from the API path while keeping the auth token and headers unchanged, aligning the client with the updated backend route.
Add tests for the CaseDetailField component using Vitest and React Testing Library. Covers: rendering label and value, null/undefined values showing "--", error state rendering a mocked ErrorIndicator, and loading state hiding the value. Tests wrap the component in ThemeProvider (oxygen-ui) to provide theming.
Collapse a multi-line logger.debug call into a single-line string in useGetProductUpdateLevels.ts for improved formatting/readability; this is a non-functional change.
Add Vitest unit tests for the CaseDetailsActionRow component. Tests verify normal render shows the assigned engineer, the "Support Engineer" label and all action buttons; loading state shows skeletons while retaining action controls; and error state renders the ErrorIndicator (mocked). Tests wrap the component in ThemeProvider to match the UI theme.
Add a new test file for the CaseDetailsBackButton component at apps/customer-portal/webapp/src/components/support/case-details/header/__tests__/CaseDetailsBackButton.test.tsx. The tests (using vitest and React Testing Library) verify that the component renders the "Back to Support Center" label and calls the provided onClick handler when clicked. The component is wrapped with ThemeProvider/createTheme from @wso2/oxygen-ui. File includes project license header.
Add a new test file for CaseDetailsPage (apps/customer-portal/webapp/src/pages/__tests__/CaseDetailsPage.test.tsx). The tests mock react-router, loader and error banner contexts, and the useGetCaseDetails hook to verify the page renders the "Back to Support Center" text during initial loading and displays skeletons when isFetching (refresh) is true. Uses Vitest and React Testing Library.
Add a JSDoc comment above getMockCaseClassification describing that it returns a mock case classification response and accepts a CaseClassificationRequest parameter. Improves inline documentation for mock functions in the customer-portal webapp.
Introduce a CaseDetailsTabConfig interface and CASE_DETAILS_TABS array to centralize labels and icons for case detail tabs (Activity, Details, Attachments, Calls, Knowledge Base). Import additional oxygen UI icons (BookOpen, Info, Paperclip, Phone) and wire them into the new config to drive the support UI tabs.
Introduce a new DetailRow React component for the customer-portal case details header. The component renders a label and formatted value, and handles loading and error states (shows a skeleton box or ErrorIndicator). Written in TypeScript with a DetailRowProps interface and uses Box/Typography from oxygen-ui and a formatValue utility.
Introduce a new CaseDetailsTabPanels React component that renders tab panel content for case details (Activity, Details, Attachments, Calls, Knowledge Base) using Box and Typography from @wso2/oxygen-ui. Adds CaseDetailsTabPanelsProps (activeTab, data, isError, isLoading) and provides placeholder content per tab index; unknown indices return null. File includes Apache 2.0 license header and references the CaseDetails type.
Introduce a new CaseDetailsBackButton component used in the case details header. The component renders an Oxygen UI Button with an ArrowLeft icon and the label "Back to Support Center", accepts an onClick handler and an optional sx prop with sensible defaults (mb, ml, alignSelf). File added at apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsBackButton.tsx.
Introduce a new Vitest test suite for the CaseDetailsHeader component. Adds mocks for getSeverityColor and ErrorIndicator, wraps the component with ThemeProvider/createTheme, and includes tests verifying normal rendering (case number, title, severity, status chip), loading state skeletons, and error indicator rendering when isError is true.
Add a new vitest/react-testing-library test file for CaseDetailsTabPanels. The tests render the component wrapped in ThemeProvider and assert the placeholder texts for activeTab values 0 (Activity), 1 (Details), 2 (Attachments), 3 (Calls) and 4 (Knowledge Base). File added at apps/customer-portal/webapp/src/components/support/case-details/header/__tests__/CaseDetailsTabPanels.test.tsx.
Add two TypeScript path mappings in apps/customer-portal/webapp/tsconfig.app.json: `@case-details` -> `src/components/support/case-details/header` and `@case-details/*` -> `src/components/support/case-details/header/*`. This enables concise imports for the case details header component within the customer-portal webapp.
Add a formatValue utility to normalize display values (null/undefined/empty -> "--") and JSDoc comments. Tighten getStatusIcon typing to accept a size prop and introduce getStatusIconElement that returns a React element via createElement with a configurable size. Also import createElement and ReactNode to support the new helper.
Add a new test suite for the CaseDetailsTabs component using Vitest and React Testing Library. Tests verify that the Activity, Details, Attachments, Calls and Knowledge Base tabs render, that onChange is called when a tab is clicked, that the Focus Mode toggle button appears when an onFocusModeToggle prop is provided, and that the Exit Focus Mode label appears when focusMode is true. Tests wrap the component with ThemeProvider using createTheme.
Create unit tests for the DetailRow component (apps/customer-portal/.../DetailRow.test.tsx). Tests use vitest and @testing-library/react and wrap the component with ThemeProvider. Mocks added for ErrorIndicator and formatValue. Covers rendering label/value, null value showing "--", error state rendering the error indicator, and loading state hiding the value.
Introduce a new CaseDetailField component used in the case details header to render a single label-value row. The component accepts label, value, isLoading and isError props and handles three states: error (renders ErrorIndicator), loading (renders skeleton box), and loaded (renders formatted value via formatValue). Uses WSO2 Oxygen UI Box and Typography and includes the Apache 2.0 license header.
Introduce CaseDetailsActionRow component for the support case details header. Renders engineer avatar/initials, name with loading/error states, a "Manage case status" label, and a row of status action buttons (Escalate, Waiting on WSO2, Mark as Resolved, Close Case) with intent-specific styling. Uses WSO2 Oxygen UI components and icons, ErrorIndicator, and formatValue util; accepts assignedEngineer, engineerInitials, isError, and optional isLoading props.
Import formatValue in support.test.ts and add unit tests validating behavior for null/undefined, empty string, non-empty string, and number inputs. Confirms formatValue returns '--' for null/undefined/empty and stringified values for other inputs.

@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: 4

🤖 Fix all issues with AI agents
In
`@apps/customer-portal/webapp/src/components/support/case-details/header/__tests__/DetailRow.test.tsx`:
- Around line 17-71: This test file duplicates the behavior covered in
CaseDetailField.test.tsx; once DetailRow component duplication is resolved,
remove or consolidate DetailRow.test.tsx by either deleting it and relying on
CaseDetailField.test.tsx or extracting shared test cases into a common test
helper used by both components; locate the tests around the DetailRow render
helper and the describe("DetailRow") block and ensure any unique assertions
(e.g., error-indicator test, null value display, loading behavior) are preserved
in the consolidated suite so coverage remains the same.

In
`@apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsContent.tsx`:
- Around line 77-85: The engineerInitials computation can produce incorrect
initials when assignedEngineer contains consecutive spaces; update the logic in
the engineerInitials definition to ignore empty segments by either trimming and
splitting on one-or-more whitespace (e.g., split(/\s+/)) or by filtering the
result (e.g., .split(" ").filter(Boolean)) before mapping to the first
character, joining, uppercasing, and slicing; keep the existing typeof
assignedEngineer check and fallback "--" unchanged so assignedEngineer handling
remains robust.

In `@apps/customer-portal/webapp/src/layouts/AppLayout.tsx`:
- Line 112: In AppLayout replace the incorrect nested object assigned to the
shorthand `p` (p: isCaseDetailsPage ? { px: 3, pb: 3, pt: 0 } : 3) by spreading
directional padding into the `sx` object: use a conditional that spreads either
`{ px: 3, pb: 3, pt: 0 }` when `isCaseDetailsPage` is true or `{ p: 3 }` when
false (e.g. ... (isCaseDetailsPage ? { px:3, pb:3, pt:0 } : { p:3 })); locate
this in the AppLayout component where `sx` is built and replace the existing
`p:` entry accordingly.

In `@apps/customer-portal/webapp/src/pages/AllCasesPage.tsx`:
- Around line 107-115: The loader condition uses isCasesFetching so it stays
visible during eager pagination and background refetches; change the loader
gating in the useEffect to only show for the initial load (or pagination
intentional loads) rather than any fetch. Replace the isPageLoading calculation
used by useEffect (which references isStatsLoading, isCasesAreaLoading,
isCasesFetching) with a more granular flag such as an "initial load" boolean
derived from the query's isLoading / isInitialLoading state or a local
hasLoadedOnce flag (set to true after the first successful page load in the
fetchNextPage flow), or use isCasesAreaLoading (initial) instead of
isCasesFetching (every fetch); then update the useEffect to use that new
initial-load flag so showLoader/hideLoader only run for the first render (or
explicit pagination) and not for background refetches triggered by
fetchNextPage/window focus.
🧹 Nitpick comments (21)
apps/customer-portal/webapp/src/api/__tests__/useGetProductUpdateLevels.test.tsx (1)

137-143: Consider a small helper to reduce the repeated window type-cast boilerplate.

The same verbose cast (window as { config?: { CUSTOMER_PORTAL_BACKEND_BASE_URL?: string } }) appears four times across two tests. A tiny helper (or a shared type alias) would reduce noise and make future test additions cleaner.

// e.g., at the top of the file
type WindowWithConfig = Window & { config?: { CUSTOMER_PORTAL_BACKEND_BASE_URL?: string } };

Not blocking — purely a readability nit.

Also applies to: 175-180

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

131-138: Consider extracting the repeated window config type annotation.

The type annotation { config?: { CUSTOMER_PORTAL_BACKEND_BASE_URL?: string } } is repeated four times across the two test cases. While the current formatting improves readability and type safety, extracting it to a type alias would reduce duplication.

♻️ Optional refactor to reduce duplication

Add a type alias near the top of the test file (after imports):

+type WindowWithConfig = {
+  config?: { CUSTOMER_PORTAL_BACKEND_BASE_URL?: string };
+};
+
 const mockLogger = {

Then update both test cases:

-    const originalWindowConfig = (
-      window as { config?: { CUSTOMER_PORTAL_BACKEND_BASE_URL?: string } }
-    ).config;
-    (
-      window as { config?: { CUSTOMER_PORTAL_BACKEND_BASE_URL?: string } }
-    ).config = {
+    const originalWindowConfig = (window as WindowWithConfig).config;
+    (window as WindowWithConfig).config = {
       CUSTOMER_PORTAL_BACKEND_BASE_URL: "https://api.example.com",
     };

Also applies to: 171-178

apps/customer-portal/webapp/vite.config.ts (1)

37-39: Alias @case-details points to the header subdirectory, not the case-details root.

The alias name suggests it covers the entire case-details feature, but it resolves specifically to .../case-details/header. If non-header case-details modules are added later (e.g., tabs, content in sibling folders), the alias name will be misleading and won't cover them. Consider either pointing to the case-details root or naming the alias @case-details-header for clarity.

apps/customer-portal/webapp/tsconfig.app.json (1)

16-17: Consistent with vite.config.ts alias — same naming caveat applies.

See the related comment on vite.config.ts regarding the alias pointing to the header subdirectory rather than the case-details root.

apps/customer-portal/webapp/src/layouts/AppLayout.tsx (1)

53-55: Consider using route matching instead of a regex on pathname.

Hardcoding a regex to detect the case details page is fragile — it will break if the route structure changes. Since you're already using react-router, consider using useMatch or useParams to detect the route in a more maintainable way.

apps/customer-portal/webapp/src/api/useGetCaseDetails.ts (1)

26-35: JSDoc still references "for a project" but the endpoint is now project-agnostic.

The URL was changed to /cases/${caseId} (no projectId in the path), but the doc comment on Line 27 still reads "Fetches a single case by id for a project." Consider updating the JSDoc to reflect the current endpoint semantics.

apps/customer-portal/webapp/src/components/dashboard/cases-table/__tests__/CasesList.test.tsx (1)

200-241: Good click-interaction coverage. Consider adding a keyboard-navigation test.

The click tests are thorough. However, the component also adds onKeyDown handling for Enter and Space keys — consider adding a test that fires a keyboard event on the title to verify the accessibility path works end-to-end.

apps/customer-portal/webapp/src/utils/support.ts (1)

41-46: Note: formatValue(0) returns "0", not "--".

This is likely intentional for numeric fields, but worth confirming since 0 is falsy and could be unexpected in some display contexts. If 0 should show as "--", add an explicit check.

apps/customer-portal/webapp/src/utils/__tests__/support.test.ts (1)

100-117: Good test coverage for formatValue.

Tests cover the key cases well. One edge case worth considering: formatValue(0) — since 0 is falsy, it's worth verifying it returns "0" rather than "--". The current implementation handles it correctly (via == null check, not a truthiness check), but an explicit test would document this guarantee.

Optional: add a test for falsy number 0
     it("should return string for number", () => {
       expect(formatValue(42)).toBe("42");
     });
+
+    it("should return '0' for zero", () => {
+      expect(formatValue(0)).toBe("0");
+    });
apps/customer-portal/webapp/src/pages/__tests__/CaseDetailsPage.test.tsx (1)

49-65: Tests cover loading/fetching states but miss error and success paths.

The two tests validate skeleton rendering during initial load and refresh, which is good. Consider adding tests for:

  • Error state: verify showError is called when isError: true.
  • Back navigation: verify clicking the back button calls navigate with the correct path.
  • Data rendering: verify content renders when data is returned.

These can be deferred but would strengthen confidence in the page's behavior.

apps/customer-portal/webapp/src/components/support/case-details/header/__tests__/CaseDetailsTabs.test.tsx (1)

48-60: Consider asserting the toggle callback is invoked on click.

The test verifies the Focus Mode button renders when onFocusModeToggle is provided, but doesn't verify clicking it actually fires the callback. A quick addition:

fireEvent.click(screen.getByRole("button", { name: /focus mode/i }));
expect(onFocusModeToggle).toHaveBeenCalled();
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsTabPanels.tsx (1)

34-89: Simplify with a data-driven approach instead of repetitive if-blocks.

Only activeTab is destructured; data, isError, and isLoading are declared in props but unused. The five near-identical if-blocks can be collapsed into an array lookup, which will also make it easier to wire real content later.

♻️ Suggested refactor
+const TAB_PLACEHOLDERS = [
+  "Activity timeline will appear here.",
+  "Details appear here.",
+  "Attachments will appear here.",
+  "Calls will appear here.",
+  "Knowledge Base articles will appear here.",
+];
+
 export default function CaseDetailsTabPanels({
   activeTab,
 }: CaseDetailsTabPanelsProps): JSX.Element | null {
   const panelBorderSx = { border: 1, borderColor: "divider", p: 2 };
+  const placeholder = TAB_PLACEHOLDERS[activeTab];
 
-  if (activeTab === 0) {
-    return (
-      <Box sx={panelBorderSx}>
-        <Typography variant="body2" color="text.secondary">
-          Activity timeline will appear here.
-        </Typography>
-      </Box>
-    );
-  }
-
-  if (activeTab === 1) {
-    ...
-  }
-  ...
-
-  return null;
+  if (!placeholder) return null;
+
+  return (
+    <Box sx={panelBorderSx}>
+      <Typography variant="body2" color="text.secondary">
+        {placeholder}
+      </Typography>
+    </Box>
+  );
 }
apps/customer-portal/webapp/src/constants/supportConstants.ts (1)

193-199: Hardcoded (0) counts in tab labels won't update dynamically.

The labels "Attachments (0)", "Calls (0)", and "Knowledge Base (0)" embed counts as static strings. When real data is wired in, these will need to become dynamic. Consider separating the base label from the count so the rendering component can compose them (e.g., label: "Attachments" with a separate count prop or formatter).

apps/customer-portal/webapp/src/components/support/case-details/header/__tests__/CaseDetailsContent.test.tsx (1)

36-37: onBack mock is never reset between tests — call counts accumulate.

vi.fn() is created once at describe scope. If a future test (or test reorder) triggers onBack before the assertion on line 104, toHaveBeenCalledTimes(1) will fail. Add a beforeEach to clear the mock.

♻️ Suggested fix
   const onBack = vi.fn();
+
+  beforeEach(() => {
+    onBack.mockClear();
+  });
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsBackButton.tsx (1)

21-24: Narrow sx type limits reusability.

The sx prop only allows mb, ml, and alignSelf. Any other MUI style property would require updating this interface. Consider using MUI's SxProps<Theme> for flexibility. Also note that the default-parameter pattern means passing a partial sx (e.g., { ml: 0 }) drops the other defaults — a spread-based merge might be more intuitive.

apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsSkeleton.tsx (1)

106-108: Hardcoded tab count could drift from CASE_DETAILS_TABS.

The skeleton renders 5 tab placeholders via a literal [1,2,3,4,5] array. If CASE_DETAILS_TABS changes length, this skeleton will be out of sync.

Proposed fix
+import { CASE_DETAILS_TABS } from "@constants/supportConstants";
 ...
-        {[1, 2, 3, 4, 5].map((i) => (
+        {CASE_DETAILS_TABS.map((_, i) => (
           <Skeleton key={i} variant="rounded" width={72} height={40} />
         ))}
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsActionRow.tsx (2)

168-180: Action buttons have no onClick handlers — confirm this is intentional placeholder UI.

All four status-action buttons (Escalate, Waiting on WSO2, Mark as Resolved, Close) render but are not wired to any behavior. If this is intentional scaffold for a future PR, consider adding disabled so users don't expect interactivity.

Proposed change
          <Button
            key={label}
            variant="outlined"
            size="small"
+           disabled
            startIcon={<Icon size={ACTION_BUTTON_ICON_SIZE} />}
            sx={getActionButtonSx(theme, paletteIntent) as Record<string, unknown>}
          >

175-175: Redundant type assertion.

getActionButtonSx already returns Record<string, unknown> (line 61), so the as Record<string, unknown> cast here is unnecessary.

Proposed fix
-            sx={getActionButtonSx(theme, paletteIntent) as Record<string, unknown>}
+            sx={getActionButtonSx(theme, paletteIntent)}
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsHeader.tsx (2)

55-113: Repetitive isError ternaries — consider a single early-return error state.

The four separate isError ? <ErrorIndicator/> : … blocks produce four identical error icons in the same row, which is visually cluttered and adds unnecessary code duplication. Mirroring the isLoading early-return pattern would simplify this:

Proposed simplification
   if (isLoading) {
     return <CaseDetailsHeaderSkeleton />;
   }
 
+  if (isError) {
+    return (
+      <Box>
+        <ErrorIndicator entityName="case details" size="small" />
+      </Box>
+    );
+  }
+
   return (
     <Box>
       <Stack
         direction="row"
         spacing={1.5}
         alignItems="center"
         sx={{ mb: 0.5, flexWrap: "wrap" }}
       >
-        {isError ? (
-          <ErrorIndicator entityName="case details" size="small" />
-        ) : (
           <Typography variant="body2" fontWeight={500} color="text.primary">
             {formatValue(caseNumber)}
           </Typography>
-        )}
-        {isError ? (
-          <ErrorIndicator entityName="case details" size="small" />
-        ) : (
           <Box sx={{ display: "flex", alignItems: "center", gap: 0.5 }}>
             ...
           </Box>
-        )}
-        {isError ? (
-          <ErrorIndicator entityName="case details" size="small" />
-        ) : (
           <Chip ... />
-        )}
       </Stack>
-      {isError ? (
-        <ErrorIndicator entityName="case details" size="small" />
-      ) : (
         <Typography variant="h6" ...>
           {formatValue(title)}
         </Typography>
-      )}
     </Box>
   );

100-100: Unsafe cast from ReactNode to ReactElement.

The statusChipIcon prop is typed as ReactNode but MUI's Chip icon prop requires ReactElement. If a caller passes null, undefined, a string, or a fragment, this cast will silently break the Chip rendering.

Consider tightening the prop type to ReactElement in CaseDetailsHeaderProps so the type system catches misuse at the call site:

- statusChipIcon: ReactNode;
+ statusChipIcon: ReactElement;
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsTabs.tsx (1)

24-24: Replace unknown with React.SyntheticEvent for the onChange event parameter to match MUI's Tabs API.

MUI's Tabs onChange prop expects (event: React.SyntheticEvent, value: any) => void. Typing the event as unknown weakens the contract and prevents callers from safely accessing event properties when needed.

Proposed fix
+import { type JSX, type SyntheticEvent } from "react";

 export interface CaseDetailsTabsProps {
   value: number;
-  onChange: (_e: unknown, newValue: number) => void;
+  onChange: (event: SyntheticEvent, newValue: number) => void;
   focusMode?: boolean;
   onFocusModeToggle?: () => void;
 }

Comment thread apps/customer-portal/webapp/src/layouts/AppLayout.tsx Outdated
Comment thread apps/customer-portal/webapp/src/pages/AllCasesPage.tsx Outdated

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

Adds a new support Case Details page and wires case-title navigation from the dashboard cases table and support cases lists, while also extending support utilities and improving test coverage around the new UI.

Changes:

  • Introduces /\:projectId/support/cases/:caseId route + CaseDetailsPage and supporting case-details header UI components.
  • Makes case titles optionally clickable in lists/tables and adds navigation tests for the new behavior.
  • Refactors/adjusts several API hooks/tests (including changing useGetCaseDetails to call /cases/:caseId) and adds utility helpers (formatValue, status icon element helper).

Reviewed changes

Copilot reviewed 44 out of 44 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
apps/customer-portal/webapp/vite.config.ts Adds @case-details Vite alias for case-details components.
apps/customer-portal/webapp/tsconfig.app.json Adds TS path mappings for @case-details.
apps/customer-portal/webapp/src/utils/support.ts Adds formatValue, refines status icon typing, adds getStatusIconElement.
apps/customer-portal/webapp/src/utils/tests/support.test.ts Adds unit tests for formatValue.
apps/customer-portal/webapp/src/pages/tests/CaseDetailsPage.test.tsx New tests for Case Details loading/skeleton behavior.
apps/customer-portal/webapp/src/pages/SupportPage.tsx Enables clicking outstanding cases to navigate to case details.
apps/customer-portal/webapp/src/pages/CaseDetailsPage.tsx New Case Details page with loader + error banner integration.
apps/customer-portal/webapp/src/pages/AllCasesPage.tsx Adds global loader handling and case-click navigation to details.
apps/customer-portal/webapp/src/models/mockFunctions.ts Minor doc/comment addition.
apps/customer-portal/webapp/src/layouts/AppLayout.tsx Adjusts padding for case details route.
apps/customer-portal/webapp/src/constants/supportConstants.ts Adds case details tab config/constants (labels + icons).
apps/customer-portal/webapp/src/components/support/support-overview-cards/tests/OutstandingCasesList.test.tsx Adds tests for card click navigation callback.
apps/customer-portal/webapp/src/components/support/support-overview-cards/OutstandingCasesList.tsx Adds optional onCaseClick to make cards navigable.
apps/customer-portal/webapp/src/components/support/case-details/header/tests/DetailRow.test.tsx New tests for DetailRow.
apps/customer-portal/webapp/src/components/support/case-details/header/tests/CaseDetailsTabs.test.tsx New tests for CaseDetailsTabs.
apps/customer-portal/webapp/src/components/support/case-details/header/tests/CaseDetailsTabPanels.test.tsx New tests for CaseDetailsTabPanels.
apps/customer-portal/webapp/src/components/support/case-details/header/tests/CaseDetailsSkeleton.test.tsx New tests for CaseDetailsSkeleton.
apps/customer-portal/webapp/src/components/support/case-details/header/tests/CaseDetailsHeader.test.tsx New tests for CaseDetailsHeader.
apps/customer-portal/webapp/src/components/support/case-details/header/tests/CaseDetailsContent.test.tsx New tests for CaseDetailsContent (tabs, focus mode, back).
apps/customer-portal/webapp/src/components/support/case-details/header/tests/CaseDetailsBackButton.test.tsx New tests for back button component.
apps/customer-portal/webapp/src/components/support/case-details/header/tests/CaseDetailsActionRow.test.tsx New tests for action row (loading/error).
apps/customer-portal/webapp/src/components/support/case-details/header/tests/CaseDetailField.test.tsx New tests for CaseDetailField.
apps/customer-portal/webapp/src/components/support/case-details/header/DetailRow.tsx New label/value row component for case details.
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsTabs.tsx New tabs component driven by CASE_DETAILS_TABS.
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsTabPanels.tsx New tab panel placeholder content.
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsSkeleton.tsx New skeleton layout for case details.
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsHeader.tsx New header component with severity/status presentation.
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsContent.tsx New composite layout (sticky header, focus mode, tabs).
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsBackButton.tsx New “Back to Support Center” button component.
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailsActionRow.tsx New assigned engineer + action buttons row.
apps/customer-portal/webapp/src/components/support/case-details/header/CaseDetailField.tsx New label/value field component for details panel.
apps/customer-portal/webapp/src/components/support/all-cases/AllCasesList.tsx Adds optional onCaseClick for navigable case cards.
apps/customer-portal/webapp/src/components/dashboard/cases-table/tests/CasesTable.test.tsx Mocks useNavigate and tests navigation triggered by case click.
apps/customer-portal/webapp/src/components/dashboard/cases-table/tests/CasesList.test.tsx Adds tests for onCaseClick behavior on case title.
apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsx Passes onCaseClick handler down to list for navigation.
apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesList.tsx Makes case title optionally clickable + keyboard accessible.
apps/customer-portal/webapp/src/api/useGetProductUpdateLevels.ts Minor logging formatting change.
apps/customer-portal/webapp/src/api/useGetCaseDetails.ts Changes request URL to /cases/:caseId.
apps/customer-portal/webapp/src/api/useGetCaseAttachments.ts Flattens enabled condition formatting.
apps/customer-portal/webapp/src/api/tests/useGetUpdatesStats.test.tsx Formatting refactor of type assertions/window config usage.
apps/customer-portal/webapp/src/api/tests/useGetProductUpdatesStats.test.tsx Formatting refactor of hook rendering/window config usage.
apps/customer-portal/webapp/src/api/tests/useGetProductUpdateLevels.test.tsx Formatting refactor of window config usage.
apps/customer-portal/webapp/src/api/tests/useGetCaseAttachments.test.tsx Formatting refactor of renderHook call.
apps/customer-portal/webapp/src/App.tsx Adds nested support/cases/:caseId route for case details page.
Comments suppressed due to low confidence (1)

apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesList.tsx:161

  • When onCaseClick is not provided, the case title is no longer interactive, but the external-link icon is still always rendered. This is misleading for users (and assistive tech) because it suggests navigation. Consider rendering the icon only when the title is actually clickable (or otherwise indicate non-interactive state).
                      >
                        {row.title || "--"}
                        <ExternalLink size={12} style={{ opacity: 0.5 }} />
                      </Typography>

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/customer-portal/webapp/src/pages/CaseDetailsPage.tsx
Comment thread apps/customer-portal/webapp/src/pages/AllCasesPage.tsx Outdated
Comment thread apps/customer-portal/webapp/src/utils/support.ts Outdated
Comment thread apps/customer-portal/webapp/src/api/useGetCaseDetails.ts
dileepapeiris and others added 7 commits February 12, 2026 11:58
Improve robustness and UX across case pages:

- CaseDetailsContent: normalize assignedEngineer before extracting initials by trimming, splitting on whitespace and filtering empty parts to handle extra/multiple spaces.
- AppLayout: adjust sx padding logic to spread conditional styles so padding keys are applied at the expected top-level shape (fixes style/typing inconsistencies).
- AllCasesPage: change loading logic to show the global loader only for the initial load (first stats + cases response) instead of during background refetches or pagination; remove unused isFetching and rename the flag to isInitialPageLoading to make intent clear.
- Tests: remove obsolete DetailRow.test.tsx.

These changes fix edge cases in initials generation, prevent spurious global loaders during background fetches, and tidy layout styling behavior.
CaseDetailsContent: trim and split assignedEngineer using /\s+/ and filter(Boolean) before mapping to initials so extra spaces or empty segments don't produce incorrect initials.

AppLayout: adjust sx padding logic to spread a conditional object ({ px, pb, pt } vs { p }) so the p styling is applied consistently depending on isCaseDetailsPage.

Tests: remove obsolete DetailRow.test.tsx.
Stop treating background fetches as page-loading. Remove isCasesFetching from the page-loading calculation, rename isPageLoading to isInitialPageLoading, and update the effect/dependency list so the global loader is shown only until the first stats and cases responses arrive (not on refetches or fetchNextPage). This prevents the loader from appearing during background updates.
Refactor case details header: CaseDetailsTabPanels no longer receives case data, loading or error flags and only takes activeTab. Updated CaseDetailsContent to match by passing only activeTab. Removed the DetailRow component (deleted file) and cleaned up unused imports/props and JSDoc in CaseDetailsTabPanels. This simplifies the tab panel responsibilities and removes an unused/detail-level component.
Replace the inline sx prop type with SxProps<Theme> in CaseDetailsBackButton for proper typing from @wso2/oxygen-ui. Update the CaseDetailsTabPanels test to only pass the required activeTab prop, removing unnecessary/undefined props to match the component's expected signature.
Extract CaseStatusAction, CaseStatusPaletteIntent and CASE_STATUS_ACTIONS from CaseDetailsActionRow into supportConstants. Update component to import the actions and the palette intent type, remove duplicate local definitions and unused icon imports, and keep styling function referencing the exported intent type. This centralizes action config and icon imports for reuse and reduces duplication.

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

Nothing major, just conventional/readability changes

@dileepapeiris

Copy link
Copy Markdown
Contributor Author

Those changes will fix in next PR.

@v15a1 v15a1 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. Please address these changes if applicable in the next PR.

@v15a1
v15a1 merged commit b79f3ec into wso2-open-operations:customer-portal-milestone-1 Feb 12, 2026
1 check passed
@dileepapeiris dileepapeiris moved this from Done to Staging Deployed in Customer Portal Development Feb 12, 2026
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/Task General task that does not fit into other categories 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