[Customer Portal] [web] Refactor Customer Portal Navigation for /projects/:projectId Routes and Add Operations & Engagements Pages - #331
Conversation
Nest project routes under 'projects/:projectId' (was '/:projectId') and import new page components. Register a dedicated OperationsPage at 'operations' and replace the generic ProjectPage-based engagements route with EngagementsPage. These changes organize project-scoped routes and introduce dedicated pages for operations and engagements.
Update the handleBack navigation to use the correct route prefix. The path was changed from `/${projectId}/security-center` to `/projects/${projectId}/security-center` so the back button navigates to the project's security center route.
Update navigation paths to include the /projects/{projectId} prefix. Adjusts routes in CreateServiceRequestPage and DescribeIssuePage so service-request list/detail and chat/create-case navigate to /projects/{projectId}/... instead of /{projectId}/.... This aligns navigation with the updated routing structure.
Update CreateCasePage navigation to use project-scoped routes by prepending `/projects` to constructed paths. Adjusted redirects for project cases list, individual case view, and security report analysis to use `/projects/${projectId}/...` so routing matches the application's project URL structure (file: apps/customer-portal/webapp/src/pages/CreateCasePage.tsx).
Introduce a new EngagementsPage component for the customer-portal webapp that displays a centered "Coming Soon" message. The page uses Box and Typography from @wso2/oxygen-ui to layout and style the placeholder content, and serves as a placeholder for upcoming engagements functionality. File includes the project's Apache-2.0 license header.
Update navigation calls to include the "/projects" segment (e.g. "/projects/:projectId/support...") so routes match the app's routing structure. Adjusted back navigation, create-case redirects, and conversation URL updates to use the new path format to ensure correct navigation and URL persistence.
Introduce a new OperationsPage React component for the customer portal. It fetches project details via useGetProjectDetails (using projectId from route params), renders a SupportStatGrid for operations, and conditionally shows ServiceRequestCard and ChangeRequestCard for managed cloud subscriptions. The file includes the project license header and currently uses placeholder stats/isError values.
Introduce OperationsStatKey type and OPERATIONS_STAT_CONFIGS array to define operations statistics cards. Also import CalendarDays and Server icons and configure four stat entries (activeServiceRequests, activeChangeRequests, completedThisMonth, upcomingChanges) with appropriate icons and colors to support operations-related metrics in the UI.
Update customer portal app layout navigation: add an 'Operations' item (uses Settings icon), move 'Project Details' entry below 'Security Center', enable the 'Engagements' item (uses Briefcase) and remove the old commented block. Adjust imports to include Briefcase and Settings and tidy icon usage to match the new nav order.
Import and invoke useOldUrlRedirect to handle legacy URL redirects. Broaden route matching to support project-scoped paths (e.g. /projects/:id/...) in addition to existing top-level patterns, and treat an empty pathname as the project hub. Updated regexes for case details, security report analysis, vulnerability details, pending updates, and update level details to match both project-scoped and legacy routes.
Normalize routes by prefixing navigations with /projects/<projectId> across ServiceRequestsPage, SupportPage, and UpdateLevelDetailsPage. Also remove unused Box import and the ServiceRequestCard/ChangeRequestCard components and their managed-cloud JSX block from SupportPage as part of a UI cleanup.
Normalize project-scoped navigation paths by adding the /projects/ prefix. Updated navigate calls in PendingUpdatesPage, ProjectHub, and SecurityPage so routes point to /projects/:projectId/... (dashboard, updates, pending level, and security-center). This fixes inconsistent routing and ensures links resolve to the correct project-scoped URLs.
Update navigation paths to include the `/projects` prefix. Changed AnnouncementsPage onCaseClick and CaseDetailsPage fallback/related-case navigations to use `/projects/${projectId}/...` so links match the updated route structure and resolve correctly.
Update navigation paths to include the /projects prefix for project-scoped routes. Changed navigate calls in AllConversationsPage and AnnouncementDetailsPage to use `/projects/${projectId}/...` so routing matches the new project URL structure (conversations and announcements).
Prefix support page navigation with `/projects/` to fix routing. Updated navigate calls in ChangeRequestsPage, ConversationDetailsPage, and ServiceRequestDetailsPage so links use `/projects/${projectId}/support/...` instead of `/${projectId}/support/...`, ensuring consistent project-scoped URLs.
Fix incorrect navigation paths by adding the `/projects` prefix to project-scoped support routes. Updated navigate calls in AllCasesPage and ChangeRequestDetailsPage (three locations) from `/${projectId}/support/...` to `/projects/${projectId}/support/...` so links correctly route to the project's support/change-request/case pages.
Build the target URL from filtered path segments and explicitly use the /projects/:id base when switching projects. Previously the code used slice(2) on the raw pathname and navigated to `/${project.id}/...`, which could produce incorrect routes; now it finds the "projects" segment, constructs the subPath after the project id, and navigates to `/projects/${project.id}/${subPath || 'dashboard'}` to be more robust to varying pathname shapes.
Update navigation paths to use the /projects/{projectId} prefix (SearchBar case links and SideBar links) to match the app's routing structure. Adjust SideBar path parsing to locate the projectId only when it follows a "projects" segment so active item detection is correct. This fixes incorrect navigation URLs and active-link highlighting.
Update GetHelpDropdown navigation paths to include the /projects/ prefix. Replaced `/${projectId}/...` with `/projects/${projectId}/...` for chat (describe-issue and create-case with state) and service-requests/create so routes align with the app's project URL structure.
Fix routing for support links by adding the missing '/projects' prefix. Updated navigation paths in CasesTable (case row click) and CasesTableHeader (chat/create-case flows). Preserves existing navigation state (skipChat) for the create-case route.
Detect projectId via useParams and prefix navigation with /projects/{projectId} when available. ServiceRequestCard now builds a base path and conditionally navigates to project-scoped or global support routes. AllUpdatesTab and UpdateProductGrid navigation calls updated to use /projects/{projectId}/... for pending updates to ensure correct project-scoped routing.
Update routing to use the /projects/{projectId} prefix for security report pages and change-request navigation (SecurityReportAnalysis, ChangeRequestCard). In ChangeRequestCard, read projectId from route params and build a base path so the secondary button navigates to /projects/{projectId}/support/change-requests when available. Adjust EditCaseAttachmentModal UI: add title capitalization and overflow handling, increase close icon size, and refine DialogContent/DialogActions paddings and Box margins for improved spacing and truncation.
Change Button variant from "contained" to "outlined" in deployment-related components to standardize button styling and reduce visual prominence. Affected files: DeploymentDocumentList.tsx, DeploymentHeader.tsx, DeploymentProductList.tsx.
Tweak EditDeploymentAttachmentModal UI: add title textTransform and overflow/ellipsis to prevent overflow, adjust DialogContent padding and Box margins, wrap description TextField in a Box and add spacing, and fine-tune DialogActions padding. Also fix ProjectCard navigation path to use /projects/{id}/dashboard instead of /{id}/dashboard.
📝 WalkthroughWalkthroughSwitches routing to project-scoped paths ( Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
apps/customer-portal/webapp/src/components/common/side-nav-bar/SideBar.tsx (1)
42-48:⚠️ Potential issue | 🟡 MinorTests need updating for new route structure with
/projects/prefix.The component now generates links with
/projects/${projectId}/${item.path}format (e.g.,/projects/project-1/dashboard), but the test file expects the old format without the/projects/prefix:
- Line 144 expects
href="/project-1/dashboard"but will get/projects/project-1/dashboard- Test setup (lines 122, 149, 156) uses pathnames without
/projects/segment, causing the active item detection logic to fail matching (since it searches for the "projects" segment)Update test pathnames to
/projects/project-1/dashboard,/projects/project-1, and/projects/project-1/support/tickets, and update the href expectations accordingly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/common/side-nav-bar/SideBar.tsx` around lines 42 - 48, Tests are failing because SideBar now builds routes with a /projects/ segment and detects activeItem by locating the "projects" path segment (see projectsIndex, projectIdIndex, activeItem in SideBar); update the test fixtures and expectations to include the /projects/ prefix: change any pathname setup values to "/projects/project-1/dashboard", "/projects/project-1", and "/projects/project-1/support/tickets" and update expected href assertions (e.g., expect href="/projects/project-1/dashboard" instead of "/project-1/dashboard") so the active-item detection and link comparisons align with the new route structure.apps/customer-portal/webapp/src/components/common/header/GetHelpDropdown.tsx (1)
65-82:⚠️ Potential issue | 🟠 MajorFinish the route migration for the Security Report action.
Lines 89-90 still navigate to
/${projectId}/support/security-report/create, while the other menu items now use/projects/${projectId}/.... Clicking Security Report will keep sending users to the legacy URL.🛠️ Proposed fix
const handleSecurityReport = () => { handleClose(); if (projectId) { navigate( - `/${projectId}/support/security-report/create`, + `/projects/${projectId}/support/security-report/create`, ); } };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/common/header/GetHelpDropdown.tsx` around lines 65 - 82, The Security Report handler still uses the legacy route; update the navigation in the Security Report click handler (e.g., handleSecurityReport) to use the migrated path pattern `/projects/${projectId}/support/security-report/create` (consistent with handleIssue and handleServiceRequest), ensuring you still call handleClose() and guard on projectId before calling navigate.apps/customer-portal/webapp/src/pages/CaseDetailsPage.tsx (1)
88-91:⚠️ Potential issue | 🟡 MinorInconsistent routing pattern for security report navigation.
The security report navigation at line 90 uses the old routing pattern
/${projectId}/security-center, while lines 93 and 99 use the new/projects/${projectId}/...prefix. This inconsistency may cause navigation failures.🔧 Proposed fix
if (isSecurityReport) { navigate( - `/${projectId}/security-center?tab=${SecurityTab.VULNERABILITIES}`, + `/projects/${projectId}/security-center?tab=${SecurityTab.VULNERABILITIES}`, ); } else {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/pages/CaseDetailsPage.tsx` around lines 88 - 91, The navigate call inside the isSecurityReport branch uses the old route pattern ("/${projectId}/security-center") causing inconsistent routing; update the navigate invocation so it uses the new route prefix "/projects/${projectId}/security-center?tab=..." (preserving SecurityTab.VULNERABILITIES and the projectId variable) to match the other navigations in this file and ensure consistent behavior for isSecurityReport.apps/customer-portal/webapp/src/pages/DescribeIssuePage.tsx (1)
79-79:⚠️ Potential issue | 🟡 MinorInconsistent routing pattern in
handleBackfallback.Line 79 uses the old routing pattern
/${projectId}/dashboard, but lines 105 and 125 use the new/projects/${projectId}/...prefix. This fallback path should be updated for consistency.🔧 Proposed fix
- navigate(projectId ? `/${projectId}/dashboard` : "/"); + navigate(projectId ? `/projects/${projectId}/dashboard` : "/");🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/pages/DescribeIssuePage.tsx` at line 79, The fallback route used in handleBack is inconsistent with the newer routing pattern; update the navigate call inside handleBack to use the /projects/${projectId}/dashboard pattern instead of `/${projectId}/dashboard`. Locate the handleBack function where navigate(projectId ? `/${projectId}/dashboard` : "/") is called and change the project path to use the /projects/${projectId}/dashboard format while keeping the root fallback "/" unchanged.apps/customer-portal/webapp/src/pages/SupportPage.tsx (1)
200-219:⚠️ Potential issue | 🟠 MajorUpdate the conversation-details branch to the new route too.
action === "resume"was migrated, but theelsebranch on Line 215 still navigates to the legacy/${projectId}/...URL. That leaves one chat action taking a redirect hop, and it can drop theconversationSummarystate passed on Line 217 if the redirect hook does not forwardlocation.state.🔧 Proposed fix
if (action === "resume") { navigate(`/projects/${projectId}/support/chat/${chatId}`); } else { navigate( - `/${projectId}/support/conversations/${chatId}`, + `/projects/${projectId}/support/conversations/${chatId}`, { state: { conversationSummary: summary }, }, ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/pages/SupportPage.tsx` around lines 200 - 219, The else branch of the onItemAction handler still navigates to the legacy path `/${projectId}/support/conversations/${chatId}`, which causes a redirect and risks losing location.state; update the navigate call in that branch (inside the onItemAction handler where projectId, chatId, action, chatItems and conversationSummary are referenced) to use the new route format `/projects/${projectId}/support/conversations/${chatId}` and retain the state object { conversationSummary: summary } so the conversation-details route receives the passed summary without a redirect hop.
🧹 Nitpick comments (4)
apps/customer-portal/webapp/src/components/project-details/deployments/EditDeploymentAttachmentModal.tsx (1)
157-163:textOverflow: "ellipsis"requireswhiteSpace: "nowrap"for single-line truncation.Without
whiteSpace: "nowrap", the text will wrap instead of truncating with an ellipsis. If the intent is to handle long dynamic titles in the future, add the missing property.Suggested fix
sx={{ pr: 6, position: "relative", textTransform: "capitalize", overflow: "hidden", textOverflow: "ellipsis", + whiteSpace: "nowrap", }}🤖 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/deployments/EditDeploymentAttachmentModal.tsx` around lines 157 - 163, The style object passed in the sx prop in EditDeploymentAttachmentModal (the block with pr: 6, position: "relative", textTransform: "capitalize", overflow: "hidden", textOverflow: "ellipsis") is missing whiteSpace: "nowrap" so the ellipsis won't work; update that sx object to include whiteSpace: "nowrap" to enforce single-line truncation for long titles.apps/customer-portal/webapp/src/pages/PendingUpdatesPage.tsx (1)
95-106: Add a guard forprojectIdinhandleViewfor consistency.Unlike
handleBack, thehandleViewcallback doesn't guard against undefinedprojectId. While the route context likely ensures it's defined, adding a guard would be consistent with the pattern used elsewhere in this file.Suggested fix
const handleView = useCallback( (levelKey: string) => { + if (!projectId) return; const params = new URLSearchParams({ productName, productBaseVersion, startingUpdateLevel: String(startingUpdateLevel), endingUpdateLevel: String(endingUpdateLevel), }); navigate(`/projects/${projectId}/updates/pending/level/${levelKey}?${params}`); }, [navigate, projectId, productName, productBaseVersion, startingUpdateLevel, endingUpdateLevel], );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/pages/PendingUpdatesPage.tsx` around lines 95 - 106, The handleView callback doesn’t guard against an undefined projectId before calling navigate; update the handleView function to check projectId (similar to handleBack) and return early if falsy, so navigate(`/projects/${projectId}/...`) is only called when projectId is defined; reference the handleView callback and the navigate call to locate where to add the guard.apps/customer-portal/webapp/src/components/support/request-cards/ServiceRequestCard.tsx (1)
32-61: Navigation works correctly, same unreachable fallback pattern as ChangeRequestCard.The primary navigation paths are correct. Since this component is only rendered in
OperationsPagewhereprojectIdis always available, the fallback paths are unreachable. Consider the same simplification as suggested forChangeRequestCard.♻️ Optional simplification
export default function ServiceRequestCard(): JSX.Element { const navigate = useNavigate(); const { projectId } = useParams<{ projectId: string }>(); - const base = projectId ? `/projects/${projectId}/support` : "support"; return ( <RequestCard ... footerButtons={[ { label: "View my requests", - onClick: () => - navigate(projectId ? `${base}/service-requests?createdByMe=true` : "service-requests?createdByMe=true"), + onClick: () => + navigate(`/projects/${projectId}/support/service-requests?createdByMe=true`), }, { label: "View all requests", - onClick: () => - navigate(projectId ? `${base}/service-requests` : "service-requests"), + onClick: () => + navigate(`/projects/${projectId}/support/service-requests`), }, ]} primaryButton={{ label: "New Service Request", - onClick: () => - navigate(projectId ? `${base}/service-requests/create` : "service-requests/create"), + onClick: () => + navigate(`/projects/${projectId}/support/service-requests/create`), icon: Server, }} /> ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/support/request-cards/ServiceRequestCard.tsx` around lines 32 - 61, ServiceRequestCard uses a fallback path for projectId that is never reached in OperationsPage; simplify by removing the unreachable branches: assume projectId exists (from useParams) and set base to `/projects/${projectId}/support`, then update all navigate calls in RequestCard footerButtons and primaryButton to use the project-scoped paths (e.g., `${base}/service-requests`, `${base}/service-requests?createdByMe=true`, `${base}/service-requests/create`) instead of ternary expressions that reference the unreachable "service-requests" fallback.apps/customer-portal/webapp/src/components/support/request-cards/ChangeRequestCard.tsx (1)
32-48: Navigation works correctly, but the fallback path is unreachable dead code.Since
ChangeRequestCardis only rendered withinOperationsPage(at/projects/:projectId/operations),projectIdwill always be defined from route params. The fallback to relative"change-requests"navigation is unreachable.This is minor since the primary path is correct, but you could simplify by removing the conditional:
♻️ Optional simplification
export default function ChangeRequestCard(): JSX.Element { const navigate = useNavigate(); const { projectId } = useParams<{ projectId: string }>(); - const base = projectId ? `/projects/${projectId}/support` : "support"; return ( <RequestCard ... secondaryButtonLabel="View All Change Requests" - onSecondaryClick={() => - navigate(projectId ? `${base}/change-requests` : "change-requests") - } + onSecondaryClick={() => + navigate(`/projects/${projectId}/support/change-requests`) + } /> ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/support/request-cards/ChangeRequestCard.tsx` around lines 32 - 48, The conditional fallback is dead code because ChangeRequestCard is only rendered when projectId exists; simplify by removing the ternary logic: ensure base is computed as `/projects/${projectId}/support` (using the projectId variable) and update the onSecondaryClick handler (the navigate call used in onSecondaryClick) to always navigate to `${base}/change-requests` (or directly to `/projects/${projectId}/support/change-requests`), eliminating the unreachable `"change-requests"` branch.
🤖 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/App.tsx`:
- Around line 74-81: Update navigation calls that still use the old
"/{projectId}/..." pattern to the new "/projects/{projectId}/..." path so they
match the App.tsx routes: in DescribeIssuePage.tsx replace navigate(projectId ?
`/${projectId}/dashboard` : "/") with navigate(projectId ?
`/projects/${projectId}/dashboard` : "/"); in CaseDetailsPage.tsx replace
navigate(`/${projectId}/security-center?tab=${SecurityTab.VULNERABILITIES}`)
with
navigate(`/projects/${projectId}/security-center?tab=${SecurityTab.VULNERABILITIES}`);
in SupportPage.tsx replace
navigate(`/${projectId}/support/conversations/${chatId}`) with
navigate(`/projects/${projectId}/support/conversations/${chatId}`); and in
NoveraChatPage.tsx replace
navigate(`/${projectId}/support/chat/${conversationResponse.conversationId}`)
with
navigate(`/projects/${projectId}/support/chat/${conversationResponse.conversationId}`).
In
`@apps/customer-portal/webapp/src/components/project-details/deployments/EditDeploymentAttachmentModal.tsx`:
- Around line 189-202: Remove the redundant top margin on the Description
TextField: inside the Box wrapper (the component rendering the Description
field) you should delete the TextField's sx={{ mt: 2 }} so the field uses the
Box's mt: 2 spacing; locate the TextField with id="edit-document-description"
(props: value={description}, onChange={handleDescriptionChange},
disabled={isSubmitting}) and remove its mt override to match the Name field
spacing.
In
`@apps/customer-portal/webapp/src/components/security/SecurityReportAnalysis.tsx`:
- Around line 145-149: Update the other create-security-report navigation in
GetHelpDropdown so it uses the new route format; locate the click handler (in
GetHelpDropdown component, e.g., the function used at lines ~86-93 that
currently navigates to `/${projectId}/support/security-report/create`) and
change the path to `/projects/${projectId}/support/security-report/create`,
ensuring it uses the same projectId variable and navigation method as
SecurityReportAnalysis.handleCreateReport.
In `@apps/customer-portal/webapp/src/pages/NoveraChatPage.tsx`:
- Around line 223-233: In NoveraChatPage, the navigation that currently replaces
the URL with the legacy path using conversationResponse.conversationId must be
changed to the project-scoped route; locate the navigate/replace call that
builds `/${projectId}/support/chat/${conversationResponse.conversationId}` (used
in the describe-issue flow) and update it to use
`/projects/${projectId}/support/chat/${conversationResponse.conversationId}` so
refresh/deep-linking stays on the new project-scoped route.
In `@apps/customer-portal/webapp/src/pages/OperationsPage.tsx`:
- Around line 42-55: The page currently forces a permanent error state by
hardcoding isError = true and isLoading = false when stats are just
placeholders; change the logic in OperationsPage (the variables stats and
isError and the SupportStatGrid props) to derive state from the presence of data
instead of hardcoding — for example set isError to false by default and pass
isLoading={stats === undefined} (or compute a loading/error state from your real
fetch hook) so SupportStatGrid receives a neutral loading state until an actual
fetch reports success or failure.
---
Outside diff comments:
In
`@apps/customer-portal/webapp/src/components/common/header/GetHelpDropdown.tsx`:
- Around line 65-82: The Security Report handler still uses the legacy route;
update the navigation in the Security Report click handler (e.g.,
handleSecurityReport) to use the migrated path pattern
`/projects/${projectId}/support/security-report/create` (consistent with
handleIssue and handleServiceRequest), ensuring you still call handleClose() and
guard on projectId before calling navigate.
In `@apps/customer-portal/webapp/src/components/common/side-nav-bar/SideBar.tsx`:
- Around line 42-48: Tests are failing because SideBar now builds routes with a
/projects/ segment and detects activeItem by locating the "projects" path
segment (see projectsIndex, projectIdIndex, activeItem in SideBar); update the
test fixtures and expectations to include the /projects/ prefix: change any
pathname setup values to "/projects/project-1/dashboard", "/projects/project-1",
and "/projects/project-1/support/tickets" and update expected href assertions
(e.g., expect href="/projects/project-1/dashboard" instead of
"/project-1/dashboard") so the active-item detection and link comparisons align
with the new route structure.
In `@apps/customer-portal/webapp/src/pages/CaseDetailsPage.tsx`:
- Around line 88-91: The navigate call inside the isSecurityReport branch uses
the old route pattern ("/${projectId}/security-center") causing inconsistent
routing; update the navigate invocation so it uses the new route prefix
"/projects/${projectId}/security-center?tab=..." (preserving
SecurityTab.VULNERABILITIES and the projectId variable) to match the other
navigations in this file and ensure consistent behavior for isSecurityReport.
In `@apps/customer-portal/webapp/src/pages/DescribeIssuePage.tsx`:
- Line 79: The fallback route used in handleBack is inconsistent with the newer
routing pattern; update the navigate call inside handleBack to use the
/projects/${projectId}/dashboard pattern instead of `/${projectId}/dashboard`.
Locate the handleBack function where navigate(projectId ?
`/${projectId}/dashboard` : "/") is called and change the project path to use
the /projects/${projectId}/dashboard format while keeping the root fallback "/"
unchanged.
In `@apps/customer-portal/webapp/src/pages/SupportPage.tsx`:
- Around line 200-219: The else branch of the onItemAction handler still
navigates to the legacy path `/${projectId}/support/conversations/${chatId}`,
which causes a redirect and risks losing location.state; update the navigate
call in that branch (inside the onItemAction handler where projectId, chatId,
action, chatItems and conversationSummary are referenced) to use the new route
format `/projects/${projectId}/support/conversations/${chatId}` and retain the
state object { conversationSummary: summary } so the conversation-details route
receives the passed summary without a redirect hop.
---
Nitpick comments:
In
`@apps/customer-portal/webapp/src/components/project-details/deployments/EditDeploymentAttachmentModal.tsx`:
- Around line 157-163: The style object passed in the sx prop in
EditDeploymentAttachmentModal (the block with pr: 6, position: "relative",
textTransform: "capitalize", overflow: "hidden", textOverflow: "ellipsis") is
missing whiteSpace: "nowrap" so the ellipsis won't work; update that sx object
to include whiteSpace: "nowrap" to enforce single-line truncation for long
titles.
In
`@apps/customer-portal/webapp/src/components/support/request-cards/ChangeRequestCard.tsx`:
- Around line 32-48: The conditional fallback is dead code because
ChangeRequestCard is only rendered when projectId exists; simplify by removing
the ternary logic: ensure base is computed as `/projects/${projectId}/support`
(using the projectId variable) and update the onSecondaryClick handler (the
navigate call used in onSecondaryClick) to always navigate to
`${base}/change-requests` (or directly to
`/projects/${projectId}/support/change-requests`), eliminating the unreachable
`"change-requests"` branch.
In
`@apps/customer-portal/webapp/src/components/support/request-cards/ServiceRequestCard.tsx`:
- Around line 32-61: ServiceRequestCard uses a fallback path for projectId that
is never reached in OperationsPage; simplify by removing the unreachable
branches: assume projectId exists (from useParams) and set base to
`/projects/${projectId}/support`, then update all navigate calls in RequestCard
footerButtons and primaryButton to use the project-scoped paths (e.g.,
`${base}/service-requests`, `${base}/service-requests?createdByMe=true`,
`${base}/service-requests/create`) instead of ternary expressions that reference
the unreachable "service-requests" fallback.
In `@apps/customer-portal/webapp/src/pages/PendingUpdatesPage.tsx`:
- Around line 95-106: The handleView callback doesn’t guard against an undefined
projectId before calling navigate; update the handleView function to check
projectId (similar to handleBack) and return early if falsy, so
navigate(`/projects/${projectId}/...`) is only called when projectId is defined;
reference the handleView callback and the navigate call to locate where to add
the guard.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 02658c32-cd15-4a88-88be-697756f8005d
📒 Files selected for processing (43)
apps/customer-portal/webapp/src/App.tsxapps/customer-portal/webapp/src/components/common/header/GetHelpDropdown.tsxapps/customer-portal/webapp/src/components/common/header/Header.tsxapps/customer-portal/webapp/src/components/common/header/SearchBar.tsxapps/customer-portal/webapp/src/components/common/side-nav-bar/SideBar.tsxapps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTable.tsxapps/customer-portal/webapp/src/components/dashboard/cases-table/CasesTableHeader.tsxapps/customer-portal/webapp/src/components/project-details/deployments/DeploymentDocumentList.tsxapps/customer-portal/webapp/src/components/project-details/deployments/DeploymentHeader.tsxapps/customer-portal/webapp/src/components/project-details/deployments/DeploymentProductList.tsxapps/customer-portal/webapp/src/components/project-details/deployments/EditDeploymentAttachmentModal.tsxapps/customer-portal/webapp/src/components/project-hub/project-card/ProjectCard.tsxapps/customer-portal/webapp/src/components/security/SecurityReportAnalysis.tsxapps/customer-portal/webapp/src/components/support/case-details/attachments-tab/EditCaseAttachmentModal.tsxapps/customer-portal/webapp/src/components/support/request-cards/ChangeRequestCard.tsxapps/customer-portal/webapp/src/components/support/request-cards/ServiceRequestCard.tsxapps/customer-portal/webapp/src/components/updates/all-updates/AllUpdatesTab.tsxapps/customer-portal/webapp/src/components/updates/update-cards/UpdateProductGrid.tsxapps/customer-portal/webapp/src/constants/appLayoutConstants.tsapps/customer-portal/webapp/src/constants/supportConstants.tsapps/customer-portal/webapp/src/layouts/AppLayout.tsxapps/customer-portal/webapp/src/pages/AllCasesPage.tsxapps/customer-portal/webapp/src/pages/AllConversationsPage.tsxapps/customer-portal/webapp/src/pages/AnnouncementDetailsPage.tsxapps/customer-portal/webapp/src/pages/AnnouncementsPage.tsxapps/customer-portal/webapp/src/pages/CaseDetailsPage.tsxapps/customer-portal/webapp/src/pages/ChangeRequestDetailsPage.tsxapps/customer-portal/webapp/src/pages/ChangeRequestsPage.tsxapps/customer-portal/webapp/src/pages/ConversationDetailsPage.tsxapps/customer-portal/webapp/src/pages/CreateCasePage.tsxapps/customer-portal/webapp/src/pages/CreateServiceRequestPage.tsxapps/customer-portal/webapp/src/pages/DescribeIssuePage.tsxapps/customer-portal/webapp/src/pages/EngagementsPage.tsxapps/customer-portal/webapp/src/pages/NoveraChatPage.tsxapps/customer-portal/webapp/src/pages/OperationsPage.tsxapps/customer-portal/webapp/src/pages/PendingUpdatesPage.tsxapps/customer-portal/webapp/src/pages/ProjectHub.tsxapps/customer-portal/webapp/src/pages/SecurityPage.tsxapps/customer-portal/webapp/src/pages/ServiceRequestDetailsPage.tsxapps/customer-portal/webapp/src/pages/ServiceRequestsPage.tsxapps/customer-portal/webapp/src/pages/SupportPage.tsxapps/customer-portal/webapp/src/pages/UpdateLevelDetailsPage.tsxapps/customer-portal/webapp/src/pages/VulnerabilityDetailsPage.tsx
Scope navigation paths to /projects/${projectId} for DescribeIssuePage and SupportPage to use project-scoped routes. In PendingUpdatesPage, guard handleView with an early return when projectId is missing to avoid runtime errors. In OperationsPage, set isError to false and make isLoading derive from stats === undefined instead of a hardcoded false to reflect real loading state.
Update routing and navigation to consistently use the /projects/{projectId} prefix and tidy up some UI styles.
Changes:
- tests: SideBar.test.tsx — update mocked locations and expectations to /projects/{projectId}/... paths.
- components/project-details/deployments/EditDeploymentAttachmentModal.tsx — prevent label wrapping (whiteSpace: nowrap) and remove unnecessary top margin on the multiline input.
- components/support/request-cards/ChangeRequestCard.tsx and ServiceRequestCard.tsx — simplify base path to `/projects/${projectId}/support` and remove conditional fallbacks, updating all navigate calls to use the unified base.
- pages/CaseDetailsPage.tsx — fix security center navigation to `/projects/${projectId}/security-center`.
Rationale: standardizes route structure across the app so links, navigation and tests target the nested /projects/{id} paths and fixes a text overflow issue in the edit-attachment modal. Note: some components now assume projectId is available.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/customer-portal/webapp/src/api/useGetCaseDetails.ts (1)
42-55:⚠️ Potential issue | 🔴 CriticalFix cache-key fragmentation and enabled-flag consistency.
This new hook has two related critical issues:
Cache fragmentation: The query key varies by
projectId ?? "byCaseId", but callers passprojectId || ""(different falsy handling). The same/cases/{caseId}endpoint will be cached under multiple keys:[CASE_DETAILS, "", caseId],[CASE_DETAILS, "byCaseId", caseId], and[CASE_DETAILS, "project-1", caseId]. Mutations inusePatchCase.ts:91invalidate with the realprojectId, so alternate cache aliases remain stale.Enabled flag inconsistency: The
enabledflag at line 73 does not checkprojectId, but every similar hook in the codebase (useGetProjectFilters,useGetProjectDeployments, etc.) requires!!projectId. The test at line 76 expectsisFetching === falsewhenprojectId="", but the current enabled logic will attempt to fetch anyway. This will cause the test to fail becauseuseAuthApiClientis unmocked.Use a consistent pattern: normalize cache key on
caseIdonly, or canonicalizeprojectIdbefore composing the key. Also requireprojectIdin the enabled flag to match the test expectations and the design pattern used elsewhere.♻️ Suggested changes
enabled: !!caseId && isSignedIn && !isAuthLoading, + enabled: !!caseId && !!projectId && isSignedIn && !isAuthLoading,And either simplify the query key to
[ApiQueryKeys.CASE_DETAILS, caseId]or normalize missingprojectIdvalues consistently.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/api/useGetCaseDetails.ts` around lines 42 - 55, The query in useGetCaseDetails currently builds a fragmented cache key using `projectId ?? "byCaseId"` and sets `enabled` without checking `projectId`, causing stale caches and tests to trigger real fetches; fix it by normalizing the cache key and requiring a real projectId: update the `queryKey` in useGetCaseDetails to either `[ApiQueryKeys.CASE_DETAILS, caseId]` (preferred) or consistently canonicalize `projectId` (e.g. `projectId || ""`) so it matches how mutations (`usePatchCase`) invalidate, and change the `enabled` flag logic to `!!projectId && !!caseId` (or the same projectId normalization) so the hook only runs when a valid projectId and caseId exist. Ensure these changes reference the existing symbols `queryKey`, `enabled`, `ApiQueryKeys.CASE_DETAILS`, `projectId`, `caseId`, and `useGetCaseDetails`.
🧹 Nitpick comments (1)
apps/customer-portal/webapp/src/components/support/request-cards/ChangeRequestCard.tsx (1)
32-46: Guard against missingprojectIdfrom route params.Line 32 unconditionally extracts
projectIdfrom route params, butuseParams()returns optional values. When the component is rendered outside a matchedprojects/:projectIdroute (as in the test on line 52 ofChangeRequestCard.test.tsx), this produces/projects/undefined/support/change-requests. Add a null check to prevent this:export default function ChangeRequestCard(): JSX.Element { const navigate = useNavigate(); const { projectId } = useParams<{ projectId: string }>(); + if (!projectId) { + return null; + } const base = `/projects/${projectId}/support`;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/components/support/request-cards/ChangeRequestCard.tsx` around lines 32 - 46, The component unconditionally uses useParams() to build base and navigate to `${base}/change-requests`, which produces paths like /projects/undefined when projectId is absent; update ChangeRequestCard to guard projectId (from useParams<{ projectId: string }>()), e.g., compute base only when projectId exists and handle the missing case by disabling the secondary button or using a safe fallback route (or no-op navigate) in the onSecondaryClick handler; ensure references to projectId, base, and the onSecondaryClick/navigate call are updated so tests that render the component without route params no longer produce invalid URLs.
🤖 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/pages/OperationsPage.tsx`:
- Around line 42-54: The page currently hardcodes stats = undefined causing
SupportStatGrid to always show loading skeletons; change the placeholder to a
neutral empty state by initializing stats to an empty object (e.g., {} typed as
Partial<Record<OperationsStatKey, number>>) and keep isError false so
SupportStatGrid can render its fallback ("--") values instead of a permanent
loader; update the OperationsPage.tsx initialization of stats and isError (the
variables referenced by SupportStatGrid) accordingly.
---
Outside diff comments:
In `@apps/customer-portal/webapp/src/api/useGetCaseDetails.ts`:
- Around line 42-55: The query in useGetCaseDetails currently builds a
fragmented cache key using `projectId ?? "byCaseId"` and sets `enabled` without
checking `projectId`, causing stale caches and tests to trigger real fetches;
fix it by normalizing the cache key and requiring a real projectId: update the
`queryKey` in useGetCaseDetails to either `[ApiQueryKeys.CASE_DETAILS, caseId]`
(preferred) or consistently canonicalize `projectId` (e.g. `projectId || ""`) so
it matches how mutations (`usePatchCase`) invalidate, and change the `enabled`
flag logic to `!!projectId && !!caseId` (or the same projectId normalization) so
the hook only runs when a valid projectId and caseId exist. Ensure these changes
reference the existing symbols `queryKey`, `enabled`,
`ApiQueryKeys.CASE_DETAILS`, `projectId`, `caseId`, and `useGetCaseDetails`.
---
Nitpick comments:
In
`@apps/customer-portal/webapp/src/components/support/request-cards/ChangeRequestCard.tsx`:
- Around line 32-46: The component unconditionally uses useParams() to build
base and navigate to `${base}/change-requests`, which produces paths like
/projects/undefined when projectId is absent; update ChangeRequestCard to guard
projectId (from useParams<{ projectId: string }>()), e.g., compute base only
when projectId exists and handle the missing case by disabling the secondary
button or using a safe fallback route (or no-op navigate) in the
onSecondaryClick handler; ensure references to projectId, base, and the
onSecondaryClick/navigate call are updated so tests that render the component
without route params no longer produce invalid URLs.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 20207584-5307-4a1f-8354-febf5f1ac8ef
📒 Files selected for processing (11)
apps/customer-portal/webapp/src/api/useGetCaseDetails.tsapps/customer-portal/webapp/src/components/common/header/GetHelpDropdown.tsxapps/customer-portal/webapp/src/components/common/side-nav-bar/__tests__/SideBar.test.tsxapps/customer-portal/webapp/src/components/project-details/deployments/EditDeploymentAttachmentModal.tsxapps/customer-portal/webapp/src/components/support/request-cards/ChangeRequestCard.tsxapps/customer-portal/webapp/src/components/support/request-cards/ServiceRequestCard.tsxapps/customer-portal/webapp/src/pages/CaseDetailsPage.tsxapps/customer-portal/webapp/src/pages/DescribeIssuePage.tsxapps/customer-portal/webapp/src/pages/OperationsPage.tsxapps/customer-portal/webapp/src/pages/PendingUpdatesPage.tsxapps/customer-portal/webapp/src/pages/SupportPage.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/customer-portal/webapp/src/pages/PendingUpdatesPage.tsx
- apps/customer-portal/webapp/src/components/support/request-cards/ServiceRequestCard.tsx
- apps/customer-portal/webapp/src/pages/CaseDetailsPage.tsx
- apps/customer-portal/webapp/src/components/project-details/deployments/EditDeploymentAttachmentModal.tsx
- apps/customer-portal/webapp/src/components/common/header/GetHelpDropdown.tsx
0bbd35a
into
wso2-open-operations:customer-portal-milestone-1
Description
This pull request updates the customer portal's routing structure to consistently nest all project-specific routes under
/projects/:projectId/, and refactors navigation logic and UI components to align with this new structure. Additionally, it introduces new pages and improves the UI/UX of several modal dialogs.Routing and Navigation Refactor
All project-specific routes have been updated from
/:projectId/...to/projects/:projectId/..., ensuring consistency across the app. This includes route definitions inApp.tsx, navigation calls in header, sidebar, project cards, support components, and case/security report pages. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16]Navigation logic in components such as
Header,SideBar,ProjectCard, and support request cards has been refactored to dynamically construct the new route paths, ensuring correct navigation regardless of the current location or project context. [1] [2] [3] [4]New Pages and Routes
OperationsPageandEngagementsPagecomponents, and added them as routes under the project-specific route group inApp.tsx. TheEngagementsPagereplaces the previous genericProjectPagefor the/engagementsroute. [1] [2]UI/UX Improvements to Modals and Buttons
Updated the variant of several
Buttoncomponents in deployment-related components fromcontainedtooutlinedfor a more consistent and modern look. [1] [2] [3]Enhanced the styling and layout of the
EditDeploymentAttachmentModalandEditCaseAttachmentModaldialogs, improving spacing, capitalization, and text overflow handling for better accessibility and appearance. [1] [2] [3] [4] [5] [6] [7]Support Card Navigation Enhancements
ChangeRequestCardandServiceRequestCardcomponents now use the updated project-based route structure and dynamically build navigation paths usinguseParams, ensuring correct routing whether or not a project context is present. [1] [2] [3] [4]Summary by CodeRabbit
New Features
UI/UX Improvements