Skip to content

[Customer Portal][Webapp] Show partner-style projects/cases overview for multi-project users - #1257

Merged
rksk merged 14 commits into
wso2-open-operations:mainfrom
cloby99:feat/enhancements
Jul 27, 2026
Merged

rksk merged 14 commits into
wso2-open-operations:mainfrom
cloby99:feat/enhancements

Conversation

@cloby99

@cloby99 cloby99 commented Jul 27, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Non-partner users with more than one project now see the same table-based overview as partners (Projects + Cases tables, search, export) instead of a project-card grid.
  • The Projects table shows Action Required, Outstanding, and Active Chats counts instead of Start/End Date. Both Projects and Cases are sourced from the unified /search endpoint (same as the partner view), now that GlobalSearchProject includes these per-project stat counts.
  • Added /projects and /cases full-list pages as "View More" destinations, mirroring the existing partner drill-down pages. These routes are gated by a new NonPartnerGuard, so partner users navigating there directly are redirected home instead of reaching pages meant for regular multi-project users.
  • Partner view (PartnerGlobalSearch, PartnerProjectsPage, PartnerCasesPage) is unchanged.
  • Removed the now-unused useGetProjectsPage hook after switching UserProjectsPage to /search.

Test plan

  • tsc --noEmit passes
  • eslint clean on all changed/new files
  • Existing project-hub/api test suite passes (same pre-existing, unrelated test failures reproduce identically on main — not introduced by this change)
  • Manually verify in browser:
    • Non-partner account with >1 project shows the new table overview (Action Required/Outstanding/Active Chats columns, no Start/End Date)
    • Non-partner account with exactly 1 project still redirects straight to that project's dashboard
    • Partner account view is unchanged
    • "View More" navigates to /projects and /cases, paginates correctly, and preserves the ?q= search param
    • A partner user manually navigating to /projects or /cases is redirected to /
    • Export CSV/PDF works from both the overview and full-list pages

Summary by CodeRabbit

  • New Features
    • Added unified project and case search for eligible users.
    • Added dedicated Projects and Cases result pages with filtering, pagination, and URL-persisted searches.
    • Added CSV and PDF export options for project and case results.
    • Added navigation from search results to project and case details.
    • Added project overview counters for active chats, required actions, and outstanding items.
  • Access
    • Restricted these overview pages to non-partner users.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@cloby99, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 43 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 010ee5ff-9d36-4642-b0a9-acc03698eb03

📥 Commits

Reviewing files that changed from the base of the PR and between 717056b and 3c44711.

📒 Files selected for processing (2)
  • apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx
  • apps/customer-portal/webapp/src/layouts/NonPartnerGuard.tsx
📝 Walkthrough

Walkthrough

Adds non-partner project and case search pages with URL-synchronized queries, pagination, CSV/PDF exports, result navigation, loading/error states, and authenticated routing. The project hub now renders a global project-and-case search overview for eligible users with multiple projects.

Changes

User search views

Layer / File(s) Summary
Search data and authenticated routes
apps/customer-portal/webapp/src/features/project-hub/types/globalSearch.ts, apps/customer-portal/webapp/src/layouts/NonPartnerGuard.tsx, apps/customer-portal/webapp/src/App.tsx
Adds project result counters, non-partner access handling, and guarded /projects and /cases routes.
Global search overview
apps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsx, apps/customer-portal/webapp/src/features/project-hub/pages/ProjectHub.tsx
Adds combined project and case search sections with exports, result states, row navigation, and drill-down links for eligible non-partner users.
Projects detail page
apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx
Adds URL-synchronized project search, pagination, project navigation, loading and empty states, and CSV/PDF export handling.
Cases detail page
apps/customer-portal/webapp/src/features/project-hub/pages/UserCasesPage.tsx
Adds URL-synchronized case search, pagination, case navigation, formatted metadata, loading and empty states, and CSV/PDF export handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ProjectHub
  participant UserGlobalSearch
  participant SearchAPI
  participant DetailPage
  User->>ProjectHub: Open project hub
  ProjectHub->>UserGlobalSearch: Render global search overview
  UserGlobalSearch->>SearchAPI: Query projects and cases
  SearchAPI-->>UserGlobalSearch: Return summary results
  User->>UserGlobalSearch: Select View More
  UserGlobalSearch->>DetailPage: Navigate with q parameter
  DetailPage->>SearchAPI: Request paginated results
  SearchAPI-->>DetailPage: Return project or case results
Loading

Possibly related PRs

Suggested labels: Type/New Feature

Suggested reviewers: rashmika998

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description only includes Summary and Test plan and misses most required template sections like Purpose, Goals, Approach, and Release note. Fill in the missing template sections, especially Purpose, Goals, Approach, User stories, Release note, Documentation, and Security checks.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: a partner-style overview for multi-project users.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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 (6)
apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx (1)

325-343: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Same missing row role as UserGlobalSearch.tsx.

Focusable rows with Enter/Space handlers but no role="button"/aria-label; assistive tech announces them as plain rows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx`
around lines 325 - 343, Update the TableRow rendered in the projects.map
callback on UserProjectsPage to expose its interactive behavior to assistive
technology by adding role="button" and an accessible aria-label describing
navigation to the project dashboard. Preserve the existing click, keyboard,
focus, and styling behavior.
apps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsx (3)

371-383: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clickable <tr> has no accessible role.

The row is focusable and Enter/Space-activated but exposes no interactive role, so assistive tech announces it as a plain row. Add role="button" (plus an aria-label naming the project) or make the first cell a real link.

♿ Proposed change
                       <TableRow
                         hover
                         key={project.id}
+                        role="button"
+                        aria-label={`Open project ${project.name}`}
                         onClick={() => navigate(`/projects/${project.id}/dashboard`)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsx`
around lines 371 - 383, Add an accessible interactive role to the focusable
TableRow in UserGlobalSearch, preferably setting role="button" and an aria-label
that identifies the associated project. Keep the existing Enter/Space keyboard
handling, navigation target, focusability, and styling unchanged.

106-171: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Two near-identical export controllers in one component.

handleExport/handleExportOpen/handleExportClose and their cases counterparts differ only in the fetch/download functions and error copy. A small useExportMenu({ fetchAll, downloadCsv, downloadPdf, emptyMessage, errorMessage }) hook would remove this block and the copies in UserProjectsPage.tsx / UserCasesPage.tsx.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsx`
around lines 106 - 171, Extract the duplicated export state, menu open/close
handlers, and async export flow from UserGlobalSearch.tsx into a reusable
useExportMenu hook accepting fetchAll, downloadCsv, downloadPdf, emptyMessage,
and errorMessage. Replace both handleExport and handleCasesExport controller
blocks with hook instances, then reuse the hook in UserProjectsPage.tsx and
UserCasesPage.tsx while preserving each page’s existing export functions and
messages.

367-399: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use ProjectClosureState.SUSPENDED for the global search suspension check.

This row uses project.closureState?.toLowerCase() === "suspended", while the project hub and project guard compare closureState === ProjectClosureState.SUSPENDED ("Suspended"). Since closure state is defined by the API contract, centralize this check on the enum value and pass the real label/state to the status chip instead of comparing lowercase strings.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsx`
around lines 367 - 399, The suspension check in the projects mapping should use
the `ProjectClosureState.SUSPENDED` enum value instead of lowercasing and
comparing a string. Update `isSuspended` to compare `project.closureState`
directly, and pass the actual closure state to the status chip while preserving
the `"Active"` fallback.

Source: Learnings

apps/customer-portal/webapp/src/features/project-hub/pages/UserCasesPage.tsx (1)

63-74: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

formatDate duplicates formatDateShort in UserGlobalSearch.tsx.

Same locale constants, same options, same null/NaN handling. Move it to a shared util (e.g. alongside the other case-table helpers) and import in both places.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/customer-portal/webapp/src/features/project-hub/pages/UserCasesPage.tsx`
around lines 63 - 74, Move the duplicated formatDate implementation and its
DATE_LOCALE/DATE_FORMAT_OPTIONS constants from UserCasesPage.tsx into a shared
utility near the existing case-table helpers. Export and import that shared
formatter in both UserCasesPage.tsx and UserGlobalSearch.tsx, preserving the
current locale, formatting options, and null/invalid-date fallback behavior.
apps/customer-portal/webapp/src/features/project-hub/pages/ProjectHub.tsx (1)

269-283: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Gating logic reads correctly, but note the duplicate projects fetch.

When showUserGlobalSearch is true, ProjectHub has already fetched a full page via useInfiniteProjects({ pageSize: PROJECT_HUB_PROJECTS_PAGE_SIZE }), and UserGlobalSearch immediately issues a second /projects/search call with pageSize: 5 (different query key, so no cache reuse). Every multi-project landing costs two requests. Consider having UserGlobalSearch reuse the parent's page size, or pass the already-loaded projects down.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/customer-portal/webapp/src/features/project-hub/pages/ProjectHub.tsx`
around lines 269 - 283, Avoid the duplicate projects request when
showUserGlobalSearch selects UserGlobalSearch: reuse the parent’s already-loaded
projects or pass PROJECT_HUB_PROJECTS_PAGE_SIZE into UserGlobalSearch so its
useInfiniteProjects query shares the existing data/query key. Update the
ProjectHub/UserGlobalSearch integration while preserving the current gating
behavior and search results.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/customer-portal/webapp/src/api/useGetProjects.ts`:
- Around line 170-182: Sanitize the public pagination inputs in the
useGetProjects flow before constructing the React Query key or
SearchProjectsRequest: derive normalized offset and limit values once, ensuring
the limit is positive and integral, then reuse those values in both queryKey and
pagination instead of the raw inputs.

In `@apps/customer-portal/webapp/src/App.tsx`:
- Around line 113-115: Restrict the projects and cases routes in the App route
configuration to non-partner users by wrapping UserProjectsPage and
UserCasesPage with the existing inverse-role guard or equivalent partner
redirect. Keep AuthGuard protection intact and ensure partner users cannot
directly access either route.

In
`@apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx`:
- Around line 78-90: Remove the urlQuery-to-setSearchQuery effect in
UserProjectsPage.tsx and UserCasesPage.tsx, initializing search state from
urlQuery via useState instead; update the q parameter with the functional
setSearchParams(prev => …) form while preserving unrelated parameters. Apply the
change at UserProjectsPage.tsx lines 78-90 and UserCasesPage.tsx lines 96-108,
including removal of each synchronization effect.

---

Nitpick comments:
In
`@apps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsx`:
- Around line 371-383: Add an accessible interactive role to the focusable
TableRow in UserGlobalSearch, preferably setting role="button" and an aria-label
that identifies the associated project. Keep the existing Enter/Space keyboard
handling, navigation target, focusability, and styling unchanged.
- Around line 106-171: Extract the duplicated export state, menu open/close
handlers, and async export flow from UserGlobalSearch.tsx into a reusable
useExportMenu hook accepting fetchAll, downloadCsv, downloadPdf, emptyMessage,
and errorMessage. Replace both handleExport and handleCasesExport controller
blocks with hook instances, then reuse the hook in UserProjectsPage.tsx and
UserCasesPage.tsx while preserving each page’s existing export functions and
messages.
- Around line 367-399: The suspension check in the projects mapping should use
the `ProjectClosureState.SUSPENDED` enum value instead of lowercasing and
comparing a string. Update `isSuspended` to compare `project.closureState`
directly, and pass the actual closure state to the status chip while preserving
the `"Active"` fallback.

In `@apps/customer-portal/webapp/src/features/project-hub/pages/ProjectHub.tsx`:
- Around line 269-283: Avoid the duplicate projects request when
showUserGlobalSearch selects UserGlobalSearch: reuse the parent’s already-loaded
projects or pass PROJECT_HUB_PROJECTS_PAGE_SIZE into UserGlobalSearch so its
useInfiniteProjects query shares the existing data/query key. Update the
ProjectHub/UserGlobalSearch integration while preserving the current gating
behavior and search results.

In
`@apps/customer-portal/webapp/src/features/project-hub/pages/UserCasesPage.tsx`:
- Around line 63-74: Move the duplicated formatDate implementation and its
DATE_LOCALE/DATE_FORMAT_OPTIONS constants from UserCasesPage.tsx into a shared
utility near the existing case-table helpers. Export and import that shared
formatter in both UserCasesPage.tsx and UserGlobalSearch.tsx, preserving the
current locale, formatting options, and null/invalid-date fallback behavior.

In
`@apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx`:
- Around line 325-343: Update the TableRow rendered in the projects.map callback
on UserProjectsPage to expose its interactive behavior to assistive technology
by adding role="button" and an accessible aria-label describing navigation to
the project dashboard. Preserve the existing click, keyboard, focus, and styling
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 49ed2530-3bfc-4cd5-8342-491eb90ea55f

📥 Commits

Reviewing files that changed from the base of the PR and between e0049d1 and e9ddda8.

📒 Files selected for processing (6)
  • apps/customer-portal/webapp/src/App.tsx
  • apps/customer-portal/webapp/src/api/useGetProjects.ts
  • apps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsx
  • apps/customer-portal/webapp/src/features/project-hub/pages/ProjectHub.tsx
  • apps/customer-portal/webapp/src/features/project-hub/pages/UserCasesPage.tsx
  • apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx

Comment thread apps/customer-portal/webapp/src/api/useGetProjects.ts Outdated
Comment thread apps/customer-portal/webapp/src/App.tsx Outdated
Comment thread apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx Outdated

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx`:
- Around line 70-100: In UserProjectsPage, derive a single normalizedQuery from
debouncedSearchQuery using trim(). Use normalizedQuery consistently for the URL
update, the useGetGlobalSearch filters, and the export request or
filtered-results hint, while preserving the existing empty-query behavior.

In `@apps/customer-portal/webapp/src/features/project-hub/types/globalSearch.ts`:
- Around line 52-54: Align the count-field definitions in the global search type
with the nullish handling in UserProjectsPage.tsx: mark activeChatsCount,
actionRequiredCount, and outstandingCount optional if the API may omit them, or
remove the ?? 0 fallbacks and enforce these fields at the API boundary. Ensure
the type contract matches the chosen runtime behavior.

In `@apps/customer-portal/webapp/src/layouts/NonPartnerGuard.tsx`:
- Around line 40-50: Update the authorization flow in NonPartnerGuard before the
hasPartnerAccess call to require userDetails and usable roles when loading and
error states have cleared; otherwise render the existing failure state or deny
access instead of treating missing roles as non-partner and rendering the
protected outlet. Preserve normal partner-role evaluation only for a complete
user profile.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0e84892f-3cac-402f-8948-9cb9033b3621

📥 Commits

Reviewing files that changed from the base of the PR and between e9ddda8 and 717056b.

📒 Files selected for processing (6)
  • apps/customer-portal/webapp/src/App.tsx
  • apps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsx
  • apps/customer-portal/webapp/src/features/project-hub/pages/UserCasesPage.tsx
  • apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx
  • apps/customer-portal/webapp/src/features/project-hub/types/globalSearch.ts
  • apps/customer-portal/webapp/src/layouts/NonPartnerGuard.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/customer-portal/webapp/src/App.tsx
  • apps/customer-portal/webapp/src/features/project-hub/pages/UserCasesPage.tsx
  • apps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsx

Comment thread apps/customer-portal/webapp/src/layouts/NonPartnerGuard.tsx Outdated
@rksk
rksk merged commit b5ae66b into wso2-open-operations:main Jul 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants