Repository navigation
[Customer Portal][Webapp] Show partner-style projects/cases overview for multi-project users - #1257
Conversation
|
Warning Review limit reached
Next review available in: 43 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds 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. ChangesUser search views
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
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (6)
apps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx (1)
325-343: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSame 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 winClickable
<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 anaria-labelnaming 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 winTwo near-identical export controllers in one component.
handleExport/handleExportOpen/handleExportCloseand their cases counterparts differ only in the fetch/download functions and error copy. A smalluseExportMenu({ fetchAll, downloadCsv, downloadPdf, emptyMessage, errorMessage })hook would remove this block and the copies inUserProjectsPage.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 winUse
ProjectClosureState.SUSPENDEDfor the global search suspension check.This row uses
project.closureState?.toLowerCase() === "suspended", while the project hub and project guard compareclosureState === 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
formatDateduplicatesformatDateShortinUserGlobalSearch.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 valueGating logic reads correctly, but note the duplicate projects fetch.
When
showUserGlobalSearchis true,ProjectHubhas already fetched a full page viauseInfiniteProjects({ pageSize: PROJECT_HUB_PROJECTS_PAGE_SIZE }), andUserGlobalSearchimmediately issues a second/projects/searchcall withpageSize: 5(different query key, so no cache reuse). Every multi-project landing costs two requests. Consider havingUserGlobalSearchreuse 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
📒 Files selected for processing (6)
apps/customer-portal/webapp/src/App.tsxapps/customer-portal/webapp/src/api/useGetProjects.tsapps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsxapps/customer-portal/webapp/src/features/project-hub/pages/ProjectHub.tsxapps/customer-portal/webapp/src/features/project-hub/pages/UserCasesPage.tsxapps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsx
There was a problem hiding this comment.
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
📒 Files selected for processing (6)
apps/customer-portal/webapp/src/App.tsxapps/customer-portal/webapp/src/features/project-hub/components/UserGlobalSearch.tsxapps/customer-portal/webapp/src/features/project-hub/pages/UserCasesPage.tsxapps/customer-portal/webapp/src/features/project-hub/pages/UserProjectsPage.tsxapps/customer-portal/webapp/src/features/project-hub/types/globalSearch.tsapps/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
Summary
/searchendpoint (same as the partner view), now thatGlobalSearchProjectincludes these per-project stat counts./projectsand/casesfull-list pages as "View More" destinations, mirroring the existing partner drill-down pages. These routes are gated by a newNonPartnerGuard, so partner users navigating there directly are redirected home instead of reaching pages meant for regular multi-project users.PartnerGlobalSearch,PartnerProjectsPage,PartnerCasesPage) is unchanged.useGetProjectsPagehook after switchingUserProjectsPageto/search.Test plan
tsc --noEmitpasseseslintclean on all changed/new filesmain— not introduced by this change)/projectsand/cases, paginates correctly, and preserves the?q=search param/projectsor/casesis redirected to/Summary by CodeRabbit