Skip to content

[Customer portal] [web] : Refactor Cases Table Filters, Improve Project Metadata UI, and Update Security Report Analysis Flow - #320

Merged
shayanmalinda merged 12 commits into
wso2-open-operations:customer-portal-milestone-1from
dileepapeiris:feat/apply-ui-suggestions-v2
Mar 10, 2026
Merged

shayanmalinda merged 12 commits into
wso2-open-operations:customer-portal-milestone-1from
dileepapeiris:feat/apply-ui-suggestions-v2

Conversation

@dileepapeiris

@dileepapeiris dileepapeiris commented Mar 10, 2026 •

Copy link
Copy Markdown
Contributor

Description

This pull request introduces a significant refactor and user experience improvement for the cases table and filtering UI in the customer portal. The main focus is on replacing the popover-based filter panel with an inline dropdown filter experience, simplifying the header actions, and improving the layout and accessibility of project metadata chips.

Key changes include:

Cases Table Filtering and Header Refactor:

  • Replaced the popover filter panel (FilterPopover and ActiveFilters) with a new inline dropdown filter component, CasesFilters, for a more intuitive and streamlined filtering experience. The filter controls are now displayed directly below the table header when activated. [1] [2] [3] [4]
  • Refactored the CasesTableHeader component to remove the "All" button and the complex filter state management props. The header now features a single "Filters" button (which toggles the filter dropdown or resets filters), and a "Create" button. The filter button changes to "Reset Filters" when filters are active. [1] [2]
  • Updated navigation logic for creating security report cases and general cases, ensuring the correct routes and query parameters are used. [1] [2]

Project Metadata Layout and Accessibility:

  • Improved the layout of the ProjectMetadata component by distributing columns more evenly and aligning content for better readability. [1] [2]
  • Enhanced the display of project type chips by adding a tooltip for long labels, ensuring better accessibility and consistent ellipsis styling.

Testing Updates:

  • Updated the tests for CasesTableHeader to reflect the new props and behaviors, including the removal of the "All" button and the new filter toggle logic. [1] [2]

Cases Table Filtering and Header Refactor:

  • Introduced the CasesFilters component to replace the popover filter UI with inline dropdowns, and removed related legacy filter management code from CasesTable. [1] [2] [3] [4]
  • Refactored CasesTableHeader to simplify the filter and create actions, removed unused props, and updated the filter button to toggle/reset filters as appropriate. [1] [2]
  • Updated navigation for security report case creation and general case creation to use the correct routes and state. [1] [2]

Project Metadata Layout and Accessibility:

  • Improved the layout and alignment of project metadata fields, and added tooltips and ellipsis styling for project type chips to enhance accessibility. [1] [2] [3]

Testing Updates:

  • Updated CasesTableHeader tests to match the new props and behaviors, removing tests for deleted buttons and adding tests for the new filter toggle logic. [1] [2]

Replace the old FilterPopover with an inline CasesFilters component and update the header API accordingly. Import Divider and Box to layout the new filter section and render CasesFilters when isFilterOpen is true. Remove the FilterPopover import and multiple obsolete handlers and derived values (handleFilterSearch, handleRemoveFilter, handleAllCases, handleCreateCase, activeFilterFields, mappedAppliedFilters). Update CasesTableHeader props to use isFiltersOpen and onFilterToggle (which clears filters if any) and simplify handleClearFilters. Overall this simplifies filter state handling and moves filter controls into the main table layout.
Adjust test suite to match refactored CasesTableHeader API: replace many old mock props with isFiltersOpen and onFilterToggle, update the Filters button test to expect onFilterToggle, remove tests for Create and All button handlers and the ActiveFilters rendering, and add a test that verifies the "Reset Filters" text appears when activeFiltersCount is non-zero.
Introduce a new CasesFilters React component for the customer-portal dashboard to render select-based filter dropdowns. The component accepts filters, filterFields and onFilterChange props, renders only fields of type "select", provides an "All <label>" option, and maps field options (string or {label,value}) into MenuItems. Uses @wso2/oxygen-ui components and TypeScript types for safety. Includes Apache-2.0 license header.
Update getAvailableCaseActions to no longer include the "Closed" option for CaseStatus.SOLUTION_PROPOSED, leaving only "Accept Solution" and "Reject Solution". This prevents closing a case while a solution is proposed and aligns the UI with the intended case workflow.
Replace the previous ActiveFilters integration and multiple filter handlers with a simplified header that uses isFiltersOpen and onFilterToggle. Add navigation logic (useNavigate/useParams) and getNoveraChatEnabled feature flag to route Create button to either chat describe flow or create-case with skipChat state. Update icons (RotateCcw, ChevronUp/ChevronDown) and adjust button states/labels (Filters / Reset Filters) and layout accordingly.
Wrap project-type, support-tier and SLA chips with Tooltip and an extracted chipStyle to enforce ellipsis, max width and consistent styling. Update imports to include Tooltip, adjust Grid to space columns evenly, and make item alignment responsive (created date left, type/tier centered, SLA right). Minor formatting and layout tweaks for better responsiveness and UX.
Rework the ProductVulnerabilities table header layout and filter control. Replace the filter icon/ActiveFilters integration with a single Filters button (onFilterIconClick -> onFilterToggle) that shows filter state: when activeFiltersCount > 0 it displays "Reset Filters" with a RotateCcw icon, otherwise shows a ListFilter icon and a chevron (up/down) driven by isFiltersOpen. Simplify the search TextField styling and adornments, add new icon imports (ChevronDown, ChevronUp, RotateCcw), and remove the ActiveFilters component and its related props (appliedFilters, filterFields, onRemoveFilter, onClearAll, onUpdateFilter). Update prop list to include isFiltersOpen and adjust layout for responsive spacing.
Introduce ProductVulnerabilitiesFilters React/TypeScript component to render a severity filter dropdown for product vulnerabilities. Uses @wso2/oxygen-ui FormControl, Select, MenuItem and Grid; accepts filters, severityOptions (default empty) and onFilterChange props. Selection changes call onFilterChange('severityId', value). Includes a default "All Severity" option and basic typing for FilterOption and props.
Replace the old FilterPopover-based UI with an inline ProductVulnerabilitiesFilters section and related layout changes. Update imports (add Divider, Box; remove FilterPopover/FilterField), widen filters state to accept numbers, and adjust handleUpdateFilter typing. Remove dynamicFilterFields and related handlers (handleFilterSearch, handleRemoveFilter, displayFiltersForPopover) and the metaData error flag usage. Convert applied filter values to strings for display and compute active filter fields from severityOptions. Change filter toggle behavior to clear existing filters when toggling on if any exist, and expose isFiltersOpen to the header. Overall this simplifies filter handling and moves filter controls into the component tree instead of a popover.
@dileepapeiris dileepapeiris self-assigned this Mar 10, 2026
@dileepapeiris dileepapeiris added Type/New Feature Represents a request or task for a new feature Type/Improvement Marks enhancements or improvements to existing features Type/UX Refers to user experience-related tasks or issues App/Customer Portal Area/Frontend Platform/Web labels Mar 10, 2026
@coderabbitai

coderabbitai Bot commented Mar 10, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Refactors filtering UI from popover-based to inline collapsible panels. Adds reusable select filter components (CasesFilters, ProductVulnerabilitiesFilters). Simplifies table header props to isFiltersOpen/onFilterToggle. Also adds tooltip-wrapped chips to ProjectMetadata and tweaks support status color/actions.

Changes

Cohort / File(s) Summary
Cases filtering & header
apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesFilters.tsx, apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsx, apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTableHeader.tsx, apps/customer-portal/webapp/src/components/dashboard/cases-table/__tests__/CasesTableHeader.test.tsx
Added inline CasesFilters component; replaced popover filter UI with inline panel in CasesTable; simplified CasesTableHeader props to isFiltersOpen/onFilterToggle and moved create-case navigation internal; tests updated to new prop surface.
Product vulnerabilities filtering
apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesFilters.tsx, apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx, apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTableHeader.tsx
Added ProductVulnerabilitiesFilters (severity select); replaced popover with inline collapsible filter panel; expanded filter value types to `string
Project metadata UI
apps/customer-portal/webapp/src/components/project-details/project-overview/project-information/ProjectMetadata.tsx
Wrapped Project Type, Support Tier, and SLA Status chips in Tooltip; introduced chipStyle for consistent truncation/sizing and adjusted Grid alignment.
Support utilities
apps/customer-portal/webapp/src/utils/support.ts
Changed getStatusColor mapping for SOLUTION_PROPOSED and removed "Closed" from getAvailableCaseActions for that status.
Layout constant
apps/customer-portal/webapp/src/constants/appLayoutConstants.ts
Replaced Home icon with LayoutDashboard in APP_SHELL_NAV_ITEMS.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Header as CasesTableHeader
participant Table as CasesTable
participant Filters as CasesFilters
participant Parent as ParentState
Header->>Table: onFilterToggle()
Table->>Filters: render when isFiltersOpen=true
Filters->>Parent: onFilterChange(field, value)
Parent->>Table: update filters prop
Table->>Header: getActiveFiltersCount(filters)
Header->>Header: update button label/icon

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Type/Task

Suggested reviewers

  • Rashmika998
  • v15a1
  • shayanmalinda

Poem

🐰 Hopping through code, I nudge a small tweak,
Popovers fall away, inline filters we seek.
Chips wear tooltips, colors gleam anew,
Selects dance in rows — a cleaner view! 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The PR description is detailed and comprehensive, covering the main changes, refactoring efforts, and improvements made across multiple components.
Title check ✅ Passed The title accurately summarizes the main changes: refactoring Cases Table filters, improving Project Metadata UI, and updating security analysis flow—all core objectives of this PR.

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

✨ Finishing Touches
🧪 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.

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

🧹 Nitpick comments (3)
apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx (1)

150-160: Minor inconsistency between toggle condition and active filters display.

The onFilterToggle handler checks Object.keys(filters).length > 0, while the button label uses activeFiltersCount derived from appliedFilters which excludes empty values (line 131). If a user selects "All Severity" (empty string value), filters = { severityId: "" }:

  • Object.keys(filters).length === 1 → triggers handleClearFilters()
  • activeFiltersCount === 0 → button shows "Filters" (not "Reset Filters")

The UI is correct, but clicking unnecessarily calls clear. Consider aligning the check with the same logic:

♻️ Suggested alignment
 onFilterToggle={() => {
-  if (Object.keys(filters).length > 0) {
+  if (Object.values(filters).some((v) => Boolean(v))) {
     handleClearFilters();
   } else {
     setIsFilterOpen(!isFilterOpen);
   }
 }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx`
around lines 150 - 160, The toggle handler uses Object.keys(filters).length to
decide between handleClearFilters() and toggling the filter panel, which
mismatches the activeFiltersCount logic that counts only non-empty
appliedFilters; update the onFilterToggle logic to use the same non-empty-filter
check as activeFiltersCount (i.e., count activeFilterFields.filter(f =>
appliedFilters[f.id]) length or otherwise check appliedFilters values for
non-empty entries) so that it calls handleClearFilters() only when there are
actually applied (non-empty) filters; leave setIsFilterOpen and isFilterOpen
behavior unchanged.
apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTableHeader.tsx (1)

85-105: Minor redundancy: fullWidth prop and sx.width: "100%" are both set.

Both achieve the same effect. Consider removing one for clarity.

♻️ Suggested cleanup
           <TextField
              sx={{
-               width: "100%",
+               // fullWidth prop handles width
              }}
              value={searchValue}
              onChange={handleSearchChange}
              placeholder="Search CVE or component"
              size="small"
              fullWidth

Or simply remove the sx prop entirely if no other styles are needed:

-          <Box sx={{ width: "100%", flexGrow: 1 }}>
-            <TextField
-              sx={{
-                width: "100%",
-              }}
+          <Box sx={{ flexGrow: 1 }}>
+            <TextField
              value={searchValue}
              onChange={handleSearchChange}
              placeholder="Search CVE or component"
              size="small"
              fullWidth
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTableHeader.tsx`
around lines 85 - 105, The TextField in ProductVulnerabilitiesTableHeader
currently sets both fullWidth and sx={{ width: "100%" }} which is redundant;
edit the TextField (used with props searchValue and onChange=handleSearchChange)
to remove one of them — either drop the fullWidth prop or remove the width from
sx (or remove the entire sx prop if no other styles are needed) so only a single
width declarative remains.
apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesFilters.tsx (1)

29-32: Narrow filterFields to the select-only contract.

The prop type accepts every FilterField, but the render path silently drops anything whose type !== "select". That makes the exported API broader than the UI can actually render, so unsupported filters can disappear at runtime.

♻️ Suggested refactor
 import type { JSX } from "react";
 import type { SelectChangeEvent } from "@wso2/oxygen-ui";
 import type { FilterField } from "@components/common/filter-panel/FilterPopover";
 
+type SelectFilterField = Omit<FilterField, "type"> & { type: "select" };
+
 export interface CasesFiltersProps {
   filters: Record<string, string | number>;
-  filterFields: FilterField[];
+  filterFields: SelectFilterField[];
   onFilterChange: (field: string, value: string | number) => void;
 }
@@
-      {filterFields.map((field) => {
-        if (field.type !== "select") return null;
-
+      {filterFields.map((field) => {

Also applies to: 54-55

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesFilters.tsx`
around lines 29 - 32, The component accepts a broad FilterField union but only
renders fields with type === "select", so narrow the prop to the select-only
contract: change CasesFiltersProps.filterFields from FilterField[] to the
specific SelectFilterField[] (or the union variant representing type: "select")
and update any other occurrences of this prop type in the file (the other
CasesFiltersProps usages noted) and imports accordingly; ensure onFilterChange
signature remains compatible and remove/adjust any code that assumed other
FilterField variants are present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/customer-portal/webapp/src/components/dashboard/cases-table/__tests__/CasesTableHeader.test.tsx`:
- Around line 55-74: Tests render CasesTableHeader but don’t mock react-router
hooks, causing useNavigate/useParams to run without router context; add a module
mock for "react-router" at the top of this test file that returns a stubbed
useNavigate (vi.fn()) and a useParams returning { projectId: "project-1" } (same
pattern as CasesTable.test.tsx), or alternatively wrap each render(...) of
CasesTableHeader in a MemoryRouter so useNavigate/useParams have router context.

In
`@apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsx`:
- Around line 228-237: The header's active filter count and the onFilterToggle
logic treat any key in filters as active even when its value is the empty string
(""), so change the logic that computes activeFiltersCount and the toggle branch
to ignore empty/nullable values: use a filtered count like
Object.values(filters).filter(v => v !== "" && v != null).length (or a small
helper such as getActiveFiltersCount) and base CasesTableHeader's
activeFiltersCount and the conditional inside onFilterToggle on that count
instead of Object.keys(filters).length; update references to filters,
onFilterToggle, handleClearFilters, setIsFilterOpen, and CasesTableHeader
accordingly.

In
`@apps/customer-portal/webapp/src/components/project-details/project-overview/project-information/ProjectMetadata.tsx`:
- Around line 116-128: The ProjectMetadata UI renders chips even when the
metadata fetch has failed; update the rendering in ProjectMetadata.tsx to honor
isError alongside isLoading so the three chip sections (the block using
isLoading, the blocks around lines where variables like type, client, and owner
are rendered) show a single error/fallback state when isError is true.
Concretely, change the conditional logic that currently checks isLoading to
instead check if (isLoading || isError) and render a consistent fallback (e.g.,
Skeleton or an error placeholder) when isError is true; references to symbols to
update include isLoading, isError, type, getProjectTypeColor, chipStyle, Tooltip
and Chip so all three sections behave consistently when metadata request fails.

---

Nitpick comments:
In
`@apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesFilters.tsx`:
- Around line 29-32: The component accepts a broad FilterField union but only
renders fields with type === "select", so narrow the prop to the select-only
contract: change CasesFiltersProps.filterFields from FilterField[] to the
specific SelectFilterField[] (or the union variant representing type: "select")
and update any other occurrences of this prop type in the file (the other
CasesFiltersProps usages noted) and imports accordingly; ensure onFilterChange
signature remains compatible and remove/adjust any code that assumed other
FilterField variants are present.

In
`@apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx`:
- Around line 150-160: The toggle handler uses Object.keys(filters).length to
decide between handleClearFilters() and toggling the filter panel, which
mismatches the activeFiltersCount logic that counts only non-empty
appliedFilters; update the onFilterToggle logic to use the same non-empty-filter
check as activeFiltersCount (i.e., count activeFilterFields.filter(f =>
appliedFilters[f.id]) length or otherwise check appliedFilters values for
non-empty entries) so that it calls handleClearFilters() only when there are
actually applied (non-empty) filters; leave setIsFilterOpen and isFilterOpen
behavior unchanged.

In
`@apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTableHeader.tsx`:
- Around line 85-105: The TextField in ProductVulnerabilitiesTableHeader
currently sets both fullWidth and sx={{ width: "100%" }} which is redundant;
edit the TextField (used with props searchValue and onChange=handleSearchChange)
to remove one of them — either drop the fullWidth prop or remove the width from
sx (or remove the entire sx prop if no other styles are needed) so only a single
width declarative remains.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b28e8354-e758-4786-8ec6-2c0134d5f4bc

📥 Commits

Reviewing files that changed from the base of the PR and between 6fc6660 and 5e7eee8.

📒 Files selected for processing (9)
  • apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesFilters.tsx
  • apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsx
  • apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTableHeader.tsx
  • apps/customer-portal/webapp/src/components/dashboard/cases-table/__tests__/CasesTableHeader.test.tsx
  • apps/customer-portal/webapp/src/components/project-details/project-overview/project-information/ProjectMetadata.tsx
  • apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesFilters.tsx
  • apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx
  • apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTableHeader.tsx
  • apps/customer-portal/webapp/src/utils/support.ts

Add a SelectFilterField type to enforce select-only filter fields and remove the runtime type check in CasesFilters. Introduce getActiveFiltersCount in CasesTable to count only non-empty filters and use it for the header badge and clear-toggle logic. Update ProductVulnerabilitiesTable to determine active filters from activeFilterFields/appliedFilters instead of Object.keys(filters). Show skeleton UI when there is an error (isError) as well as while loading in ProjectMetadata. Simplify ProductVulnerabilitiesTableHeader by removing an unnecessary TextField sx wrapper. Update app shell icon from Home to LayoutDashboard. Also add react-router mocks in CasesTableHeader tests to avoid hook errors.

@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

🧹 Nitpick comments (6)
apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTableHeader.tsx (1)

36-36: Consider making onFilterToggle required.

The interface marks onFilterToggle as optional, but the button at line 108 relies on it without any disabled state or fallback behavior. The context snippet from ProductVulnerabilitiesTable.tsx confirms this prop is always provided.

If the toggle is always required for the button to function, removing the optional marker improves type safety and catches missing props at compile time.

🔧 Suggested fix
-  onFilterToggle?: () => void;
+  onFilterToggle: () => void;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTableHeader.tsx`
at line 36, The prop onFilterToggle is marked optional but the component
ProductVulnerabilitiesTableHeader uses it unguarded (e.g., the filter toggle
button), so remove the optional marker and make onFilterToggle required in the
component props/interface (change onFilterToggle?: () => void to onFilterToggle:
() => void) to enforce callers provide the handler; then run TypeScript checks
and update any callers that do not pass onFilterToggle (e.g.,
ProductVulnerabilitiesTable) to supply the callback if necessary.
apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx (2)

49-54: Consider handling metadata loading and error states.

Only data is destructured from useGetVulnerabilitiesMetaData, but per the hook definition (context snippet 1), it returns isError and isLoading as well. If the metadata fetch fails, severityOptions will silently be an empty array, and users won't understand why the severity dropdown has no options.

Consider destructuring at minimum isLoading to show a loading state for the filter dropdown, or isError to display feedback when metadata fails to load.

Suggested improvement
- const { data: metaData } = useGetVulnerabilitiesMetaData();
+ const { data: metaData, isLoading: isMetaLoading, isError: isMetaError } = useGetVulnerabilitiesMetaData();

Then pass isMetaLoading or isMetaError to ProductVulnerabilitiesFilters if needed for UI feedback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx`
around lines 49 - 54, Destructure isLoading and isError from
useGetVulnerabilitiesMetaData (in addition to data) and use them to avoid
silently showing an empty severityOptions: when isLoading is true, render or
pass an isMetaLoading flag to ProductVulnerabilitiesFilters so the severity
dropdown can show a loading state; when isError is true, pass an isMetaError
flag (or render an error message) so the UI can display feedback instead of an
empty list. Update the severityOptions computation to still safely derive
options from metaData but rely on the new flags to control display/feedback in
ProductVulnerabilitiesFilters.

150-162: Extract duplicated filter count calculation.

The expression activeFilterFields.filter((f) => appliedFilters[f.id]).length is computed twice (lines 151-152 and 161). Consider extracting this to a variable for clarity and to follow DRY principles.

Suggested refactor

Compute once and reuse:

+ const activeFiltersCount = activeFilterFields.filter((f) => appliedFilters[f.id]).length;

  <ProductVulnerabilitiesTableHeader
    ...
    onFilterToggle={() => {
-     if (
-       activeFilterFields.filter((f) => appliedFilters[f.id]).length > 0
-     ) {
+     if (activeFiltersCount > 0) {
        handleClearFilters();
      } else {
        setIsFilterOpen(!isFilterOpen);
      }
    }}
    isFiltersOpen={isFilterOpen}
-   activeFiltersCount={
-     activeFilterFields.filter((f) => appliedFilters[f.id]).length
-   }
+   activeFiltersCount={activeFiltersCount}
  />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx`
around lines 150 - 162, Extract the duplicated filter-count expression into a
single variable (e.g., const activeFilterCount = activeFilterFields.filter(f =>
appliedFilters[f.id]).length) near where activeFilterFields and appliedFilters
are available, then replace both occurrences used in the onFilterToggle callback
and the activeFiltersCount prop with that variable; ensure the onFilterToggle
logic still calls handleClearFilters() when activeFilterCount > 0 and toggles
isFilterOpen via setIsFilterOpen otherwise, and keep isFiltersOpen prop
unchanged.
apps/customer-portal/webapp/src/components/project-details/project-overview/project-information/ProjectMetadata.tsx (1)

119-127: Consider guarding against empty tooltip titles.

When type?.label is undefined/null, the Tooltip title becomes an empty string, which may render an awkward empty tooltip bubble on hover. The same applies to supportTier and slaStatus at lines 151 and 183.

You could skip the Tooltip wrapper when the value is empty, or this may be acceptable if these values are guaranteed by the API.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/components/project-details/project-overview/project-information/ProjectMetadata.tsx`
around lines 119 - 127, The Tooltip wrappers around the Chip components (e.g.,
the Tooltip around the Chip using type?.label with getProjectTypeColor) can
render an awkward empty bubble when the title is empty; update the render logic
to only wrap the Chip with Tooltip when the corresponding value is non-empty
(e.g., type?.label, supportTier?.label, slaStatus?.label) — otherwise render the
Chip alone — so check those values before returning the Tooltip and use the
existing Chip props (label, size, variant, color, sx) unchanged when not
wrapped.
apps/customer-portal/webapp/src/components/dashboard/cases-table/__tests__/CasesTableHeader.test.tsx (1)

79-82: Exercise the reset branch, not just the label.

This only proves the copy change. Add a click assertion so the activeFiltersCount > 0 path also verifies onFilterToggle is still wired when the button switches to Reset Filters.

🧪 Suggested test update
 it("should show Reset Filters when filters are active", () => {
-  render(<CasesTableHeader {...mockProps} activeFiltersCount={1} />);
+  const onFilterToggle = vi.fn();
+  render(
+    <CasesTableHeader
+      {...mockProps}
+      activeFiltersCount={1}
+      onFilterToggle={onFilterToggle}
+    />,
+  );

   expect(screen.getByText("Reset Filters")).toBeInTheDocument();
+  fireEvent.click(screen.getByText("Reset Filters"));
+  expect(onFilterToggle).toHaveBeenCalled();
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/components/dashboard/cases-table/__tests__/CasesTableHeader.test.tsx`
around lines 79 - 82, Render CasesTableHeader with activeFiltersCount={1} and a
jest.fn() for onFilterToggle (from mockProps), find the "Reset Filters" button
via screen.getByText("Reset Filters"), simulate a click (userEvent.click or
fireEvent.click) on that element, and assert that the onFilterToggle mock was
called to verify the reset branch wiring in the CasesTableHeader component.
apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsx (1)

25-27: Move the shared filter-field type out of FilterPopover.

CasesTable now renders CasesFilters, but its field contract still comes from the old popover module. That keeps the replacement coupled to the component it is displacing and makes the eventual FilterPopover cleanup harder.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsx`
around lines 25 - 27, The FilterField type is still imported from FilterPopover,
coupling CasesTable/CasesFilters to the old popover; create or move the
FilterField type to a shared location (e.g., a new types module for filters) and
update CasesTable and CasesFilters to import FilterField from that shared module
instead of "@components/common/filter-panel/FilterPopover"; also update the
FilterPopover file to import the type from the new shared module (and remove its
own export) so all components reference the single shared type (symbols to
change: FilterField import, CasesTable, CasesFilters, and FilterPopover).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsx`:
- Around line 232-241: The test's sibling mock still destructures the old header
callbacks (onFilterClick, onRemoveFilter, onUpdateFilter, onClearAll) but the
real CasesTable now renders CasesTableHeader with props activeFiltersCount,
isFiltersOpen, and onFilterToggle; update the mock in CasesTable.test.tsx to
accept and expose those three props instead, remove references to the old
callbacks, and rewrite assertions to exercise the new flow (simulate calling
onFilterToggle and assert isFiltersOpen behavior and the new reset behavior for
CasesFilters/reset flow) while keeping any checks for activeFiltersCount where
applicable.

In
`@apps/customer-portal/webapp/src/components/project-details/project-overview/project-information/ProjectMetadata.tsx`:
- Around line 53-62: The sx object uses an invalid key `font: "caption"`; update
the MUI sx usage to use `typography: "caption"` instead. Replace `font:
"caption"` with `typography: "caption"` in the chipStyle object inside
ProjectMetadata (symbol: chipStyle in ProjectMetadata.tsx) and make the same
change where `font: "caption"` is used in ProjectName.tsx (look for the
component or variable applying that sx). Ensure you do not alter other CSS
properties and keep the nested `& .MuiChip-label` block intact.

---

Nitpick comments:
In
`@apps/customer-portal/webapp/src/components/dashboard/cases-table/__tests__/CasesTableHeader.test.tsx`:
- Around line 79-82: Render CasesTableHeader with activeFiltersCount={1} and a
jest.fn() for onFilterToggle (from mockProps), find the "Reset Filters" button
via screen.getByText("Reset Filters"), simulate a click (userEvent.click or
fireEvent.click) on that element, and assert that the onFilterToggle mock was
called to verify the reset branch wiring in the CasesTableHeader component.

In
`@apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsx`:
- Around line 25-27: The FilterField type is still imported from FilterPopover,
coupling CasesTable/CasesFilters to the old popover; create or move the
FilterField type to a shared location (e.g., a new types module for filters) and
update CasesTable and CasesFilters to import FilterField from that shared module
instead of "@components/common/filter-panel/FilterPopover"; also update the
FilterPopover file to import the type from the new shared module (and remove its
own export) so all components reference the single shared type (symbols to
change: FilterField import, CasesTable, CasesFilters, and FilterPopover).

In
`@apps/customer-portal/webapp/src/components/project-details/project-overview/project-information/ProjectMetadata.tsx`:
- Around line 119-127: The Tooltip wrappers around the Chip components (e.g.,
the Tooltip around the Chip using type?.label with getProjectTypeColor) can
render an awkward empty bubble when the title is empty; update the render logic
to only wrap the Chip with Tooltip when the corresponding value is non-empty
(e.g., type?.label, supportTier?.label, slaStatus?.label) — otherwise render the
Chip alone — so check those values before returning the Tooltip and use the
existing Chip props (label, size, variant, color, sx) unchanged when not
wrapped.

In
`@apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx`:
- Around line 49-54: Destructure isLoading and isError from
useGetVulnerabilitiesMetaData (in addition to data) and use them to avoid
silently showing an empty severityOptions: when isLoading is true, render or
pass an isMetaLoading flag to ProductVulnerabilitiesFilters so the severity
dropdown can show a loading state; when isError is true, pass an isMetaError
flag (or render an error message) so the UI can display feedback instead of an
empty list. Update the severityOptions computation to still safely derive
options from metaData but rely on the new flags to control display/feedback in
ProductVulnerabilitiesFilters.
- Around line 150-162: Extract the duplicated filter-count expression into a
single variable (e.g., const activeFilterCount = activeFilterFields.filter(f =>
appliedFilters[f.id]).length) near where activeFilterFields and appliedFilters
are available, then replace both occurrences used in the onFilterToggle callback
and the activeFiltersCount prop with that variable; ensure the onFilterToggle
logic still calls handleClearFilters() when activeFilterCount > 0 and toggles
isFilterOpen via setIsFilterOpen otherwise, and keep isFiltersOpen prop
unchanged.

In
`@apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTableHeader.tsx`:
- Line 36: The prop onFilterToggle is marked optional but the component
ProductVulnerabilitiesTableHeader uses it unguarded (e.g., the filter toggle
button), so remove the optional marker and make onFilterToggle required in the
component props/interface (change onFilterToggle?: () => void to onFilterToggle:
() => void) to enforce callers provide the handler; then run TypeScript checks
and update any callers that do not pass onFilterToggle (e.g.,
ProductVulnerabilitiesTable) to supply the callback if necessary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ebe67bbc-53b0-4ba5-8df5-c2a50a23c6dc

📥 Commits

Reviewing files that changed from the base of the PR and between 5e7eee8 and 4aa7645.

📒 Files selected for processing (7)
  • apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesFilters.tsx
  • apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsx
  • apps/customer-portal/webapp/src/components/dashboard/cases-table/__tests__/CasesTableHeader.test.tsx
  • apps/customer-portal/webapp/src/components/project-details/project-overview/project-information/ProjectMetadata.tsx
  • apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTable.tsx
  • apps/customer-portal/webapp/src/components/security/ProductVulnerabilitiesTableHeader.tsx
  • apps/customer-portal/webapp/src/constants/appLayoutConstants.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/customer-portal/webapp/src/components/dashboard/cases-table/CasesFilters.tsx

Route security report creation to a dedicated path and tighten component props.

- SecurityReportAnalysis: navigate to /:projectId/support/security-report/create instead of using the query param + state.
- CreateCasePage: detect security report creation by checking the pathname for "security-report" in addition to the existing query param so skipChat logic still applies.
- ProductVulnerabilitiesTableHeader: make onFilterToggle a required prop (remove optional).
Add a new nested route for security-report/create and update GetHelpDropdown navigation to use the new path. Apply minor typing cast for dynamicFilterFields in CasesTable. Replace invalid sx font prop with typography and improve ProjectMetadata/ProjectName handling: show Chips without Tooltips when labels are absent and use safe color helpers. Memoize and reuse activeFilterCount in ProductVulnerabilitiesTable to avoid repeated calculations and simplify filter toggle logic.
@dileepapeiris dileepapeiris changed the title Feat/apply UI suggestions v2 Refactor Cases Table Filters, Improve Project Metadata UI, and Update Security Report Analysis Flow Mar 10, 2026
@dileepapeiris dileepapeiris changed the title Refactor Cases Table Filters, Improve Project Metadata UI, and Update Security Report Analysis Flow [Customer portal] [web] : Refactor Cases Table Filters, Improve Project Metadata UI, and Update Security Report Analysis Flow Mar 10, 2026
@shayanmalinda
shayanmalinda merged commit f66463d into wso2-open-operations:customer-portal-milestone-1 Mar 10, 2026
1 check passed
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/New Feature Represents a request or task for a new feature Type/UX Refers to user experience-related tasks or issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants