feat(customer-portal): Service Requests list, detail view, and create flow - #290
Conversation
- ServiceRequestsList: Card-based list displaying SR details with status chips, request type, metadata, and relative timestamps - ServiceRequestsSearchBar: Search input with status filter tabs (All, Pending, In Progress, Completed) - ServiceRequestsListSkeleton: Loading skeleton for the list
- Filters cases by Service Request type - Search functionality with frontend filtering - Status filter tabs (All, Pending, In Progress, Completed) - Sort by newest/oldest - Pagination support
- Add /support/service-requests route in App.tsx - Update ServiceRequestCard to navigate to the new page - Fix New Service Request button to use skipChat mode
- Add catalog types and CreateServiceRequestPayload to models - Add useSearchCatalogs and useGetCatalogItemVariables API hooks - Add CatalogSelector with category icons and VariableFormFields - Add CreateServiceRequestPage with deployment/product/catalog/variables flow - Update navigation: ServiceRequestCard, GetHelpDropdown, ServiceRequestsPage - Add route /support/service-requests/create - Extend usePostCase to accept CreateServiceRequestPayload
…terisks, SR detail styling - Use Rich Text Editor for Description fields in VariableFormFields - Add file input for attachment variables with onAttachmentAdd - Add red asterisk for required fields, remove placeholders - Extend CreateCaseResponse with backend fields (number, createdBy, etc.) - SR detail: activity timeline with vertical flow, assigned/category cards - Communication: chat icons blue (user) vs orange (team) per case chat style
- Add serviceRequestValidation utils: isAttachmentField, getFirstEmptyRequiredField - VariableFormFields: use file picker for attachment vars (by type or questionText) - Make attachment variables optional in validation - CreateServiceRequestPage: filter variables to only send non-empty values
…roved styling - Updated timeline entries to include actor information for created, commented, and closed statuses. - Adjusted layout and styling for better visual consistency, including icon sizes and background colors. - Refined the sorting logic for timeline entries based on date.
…istics - Introduced ServiceRequestsStatCards component to display statistics for service requests including pending, in progress, completed, and rejected counts. - Updated supportConstants to include configuration and valid keys for service request statistics. - Integrated ServiceRequestsStatCards into ServiceRequestsPage, utilizing memoized stats calculation based on service request statuses.
… and search parameters - Updated RequestCard component to support optional footer buttons for enhanced user interaction. - Modified ServiceRequestCard to utilize the new footer buttons for navigating service requests. - Enhanced ServiceRequestsPage to handle search parameters, allowing users to filter requests by creator. - Adjusted UI text based on the presence of search parameters for improved clarity.
…tistics display - Added dark mode detection to ServiceRequestsSearchBar using a custom hook. - Introduced stats prop to display counts for pending, in progress, completed, and rejected service requests. - Updated button labels in the status tabs to include corresponding counts for improved user feedback. - Integrated stats into the ServiceRequestsPage to provide a comprehensive overview of service request statuses.
…tent components - Streamlined the footer button layout in RequestCard by removing unnecessary borders and adjusting spacing. - Replaced custom Box components with Divider for cleaner separation in ServiceRequestDetailContent. - Removed redundant border styles and improved overall component styling for better visual consistency.
… new field handling - Updated ServiceRequestDetailContent to improve comment button styling and layout. - Introduced a new File Copy Path field in VariableFormFields for better user input handling. - Enhanced validation utilities to support the new File Copy Path field. - Updated CaseDetails model to include catalog and catalogItem references for improved data handling.
…nt and VariableFormFields - Updated ServiceRequestDetailContent to exclude sections labeled as "WSO2 Product" from display. - Enhanced VariableFormFields to hide context fields matching the "WSO2 Product" pattern from the UI. - Improved overall user experience by ensuring only relevant fields are shown in both components.
…gation - Introduced a Cancel button in the CreateServiceRequestPage to allow users to easily navigate back. - Enhanced button styling with a gap for better visual separation from the submit button.
- Removed unnecessary border radius and border color styles for improved visual consistency. - Streamlined AccordionDetails styling by consolidating properties for cleaner code.
…stPage components - Removed commented-out code in ServiceRequestDetailContent for improved readability. - Eliminated debug console log in CreateServiceRequestPage to streamline the code and reduce clutter.
- Replaced useGetCasesFilters with useGetProjectFilters for improved data handling. - Enhanced filter metadata retrieval to align with project-specific requirements.
📝 WalkthroughWalkthroughAdds a service-requests feature: new pages, nested routes, API hooks, models, validation utilities, and multiple UI components to search, create (multi-step with attachments), list, and view catalog-driven service requests. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Browser as Browser/UI
participant DeploymentsAPI as "Deployments API"
participant CatalogsAPI as "Catalogs API"
participant VariablesAPI as "Variables API"
participant CaseAPI as "Case API"
User->>Browser: Open "Create Service Request"
Browser->>DeploymentsAPI: GET /projects/:projectId/deployments
DeploymentsAPI-->>Browser: deployments list
User->>Browser: Select deployed product
Browser->>CatalogsAPI: POST /deployments/products/:deployedProductId/catalogs/search
CatalogsAPI-->>Browser: catalogs + items
User->>Browser: Select catalog item
Browser->>VariablesAPI: GET /catalogs/:catalogId/items/:itemId (variables)
VariablesAPI-->>Browser: catalog item variables
User->>Browser: Fill variables & attach files
Browser->>Browser: encode attachments (base64)
User->>Browser: Submit
Browser->>CaseAPI: POST /cases (type: service_request)
CaseAPI-->>Browser: created case (id/number)
Browser->>Browser: Navigate to /:projectId/service-requests/:serviceRequestId
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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: 10
🧹 Nitpick comments (1)
apps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestsStatCards.tsx (1)
25-30: Consider removing unusedServiceRequestStatsinterface.This interface is defined but not used within this component. The
ServiceRequestsStatCardsProps.statsproperty usesPartial<Record<ServiceRequestStatKey, number>>instead. If this interface isn't exported for use elsewhere, consider removing it to reduce dead code.♻️ Proposed fix
-export interface ServiceRequestStats { - pending: number; - inProgress: number; - completed: number; - rejected: number; -} -If the interface is needed elsewhere, ensure it's being imported and used 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/support/service-requests/ServiceRequestsStatCards.tsx` around lines 25 - 30, The ServiceRequestStats interface is unused in this file (ServiceRequestStats) while ServiceRequestsStatCardsProps.stats uses Partial<Record<ServiceRequestStatKey, number>>; either remove the dead interface ServiceRequestStats from the file or replace the props type to use ServiceRequestStats (or export/import it where needed). Locate the ServiceRequestStats declaration and either delete it to eliminate dead code or update ServiceRequestsStatCardsProps.stats to reference ServiceRequestStats (and export/import it if used across modules).
🤖 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/api/usePostCase.ts`:
- Around line 49-55: The logger.debug call in usePostCase currently spreads the
entire body (body) into logs which can leak sensitive data; replace that spread
with a sanitized summary: construct an object that includes only safe fields
(e.g., caseId, type, truncated description via the existing description slice
logic, and a boolean or masked indicator for attachments rather than attachment
contents), and log that object instead of "...body"; update the logger.debug
invocation in usePostCase to use this sanitizedPayload and consider adding a
small helper (e.g., sanitizeCasePayload) to centralize masking of
attachment-related fields and any other sensitive keys.
In
`@apps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestDetailContent.tsx`:
- Around line 274-285: The handleAddComment flow swallows errors on
postComment.mutate so failures are silent; add an onError handler to
postComment.mutate (in the handleAddComment function) that surfaces the error to
the user (e.g., set a local error state or call the app's notification/banner
utility) and optionally logs the error, while preserving the existing onSuccess
that calls setCommentText(""); reference postComment.mutate and handleAddComment
to locate the change.
- Around line 462-489: The current render logic filters requestDetailSections
(in ServiceRequestDetailContent) to remove sections matching /^wso2\s*product$/i
into the local variable filtered, but when requestDetailSections exists and
filtered becomes empty the component returns nothing; change the conditional to
check filtered.length as well and render the existing fallback content when
filtered.length === 0 (i.e., if filtered is empty, render the same fallback
branch used when requestDetailSections.length === 0), so adjust the anonymous
render block around requestDetailSections/filter/filtered to fall back to the
fallback JSX when filtered has no items.
In
`@apps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestsSearchBar.tsx`:
- Around line 125-134: The count calculation in ServiceRequestsSearchBar.tsx
incorrectly uses tab.value (e.g., "in_progress") to index ServiceRequestStats
(which has camelCase keys like inProgress), causing the "In Progress" tab to
show 0; update the logic that computes count (the const named count that
references tab.value and stats) to map the snake_case tab values to the
camelCase stats keys (either by adding a small mapping object or a conditional
for "in_progress" => stats.inProgress) so stats?.inProgress is used for that tab
while leaving other tabs to use stats?.[tab.value as keyof ServiceRequestStats]
?? 0.
In `@apps/customer-portal/webapp/src/constants/supportConstants.ts`:
- Around line 881-887: Remove the locally redeclared ChangeRequestFilterValues
interface in this file and rely on the existing interface imported from
`@models/responses` (the imported symbol ChangeRequestFilterValues). Delete the
duplicate export block, ensure the import statement for
ChangeRequestFilterValues remains (or add it if missing), and update any local
references to use the imported type so there's no shadowing or duplicate
declaration.
In `@apps/customer-portal/webapp/src/pages/CreateServiceRequestPage.tsx`:
- Around line 273-276: The submit handler currently blocks requests when
variablePayload.length === 0 by calling showError; instead allow submission for
request types that legitimately require zero user variables: remove or alter the
conditional in the submit function inside CreateServiceRequestPage so it only
validates variables when the request type indicates variables are required (use
the request type / schema check already used to render "No additional fields
required"); update the logic around variablePayload and showError so that absent
variables are only an error when the request type expects user-provided
variables (also apply the same change to the duplicate validation block around
the other check at the second location covering lines 326-333).
- Around line 152-173: The handlers handleDeploymentChange, handleProductChange,
and handleSelectCatalogItem currently reset variableValues but leave uploaded
attachments intact; update each handler to also clear the attachments state
(e.g., call setAttachments([]) or the appropriate attachment-reset function)
whenever deployment, product, or catalog selection changes so stale files are
not submitted with a different request type.
In `@apps/customer-portal/webapp/src/pages/ServiceRequestsPage.tsx`:
- Around line 111-137: srStats bucketization diverges from the tab-filtering
logic so counts and displayed results can mismatch; extract a single canonical
status-bucketing helper (e.g., getStatusBucket(statusLabel) that normalizes
sr.status.label to one of: "pending", "inProgress", "completed", "rejected") and
replace the ad-hoc logic inside the srStats useMemo and the tab filter predicate
with calls to that helper so both counting and filtering use the same mapping;
ensure the helper covers the same label checks currently used
(open/awaiting/waiting/pending → pending; progress → inProgress;
rejected/cancelled → rejected; closed/resolved/completed → completed) and update
both srStats and the tab selection/filtering code to rely on it.
- Around line 78-90: The loading logic can get stuck because it only checks data
and isLoading; update the useGetProjectCases destructure to also pull isError
(or error) and use that in isCasesAreaLoading and any other loading checks
(e.g., in the block that defines isCasesAreaLoading and the analogous area at
lines ~249-283) so that when isError is true the UI stops showing infinite
loading/skeletons; adjust isCasesAreaLoading to: const isCasesAreaLoading =
isCasesQueryLoading || (!!projectId && !hasCasesResponse && !isError) and add a
simple error/render fallback where appropriate to surface the error.
In `@apps/customer-portal/webapp/src/utils/serviceRequestValidation.ts`:
- Around line 138-146: The validator currently iterates every item returned by
getUserEditableVariables and can flag required errors for backend-duplicate
variables; to match the UI deduplication, deduplicate userEditable by their
visible question text before checking required values: map each variable v to a
normalized label (use v.questionText with leading asterisk/spaces removed and
trimmed), keep the first variable for each unique label, then iterate that
deduplicated array and apply the existing logic (skip
isAttachmentField/isFileCopyPathField checks, read value from
variableValues[v.id], and return the normalized label when empty). Ensure you
reference getUserEditableVariables, isAttachmentField, isFileCopyPathField,
variableValues and questionText when updating the function.
---
Nitpick comments:
In
`@apps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestsStatCards.tsx`:
- Around line 25-30: The ServiceRequestStats interface is unused in this file
(ServiceRequestStats) while ServiceRequestsStatCardsProps.stats uses
Partial<Record<ServiceRequestStatKey, number>>; either remove the dead interface
ServiceRequestStats from the file or replace the props type to use
ServiceRequestStats (or export/import it where needed). Locate the
ServiceRequestStats declaration and either delete it to eliminate dead code or
update ServiceRequestsStatCardsProps.stats to reference ServiceRequestStats (and
export/import it if used across modules).
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
apps/customer-portal/webapp/src/App.tsxapps/customer-portal/webapp/src/api/__tests__/useGetCaseAttachments.test.tsxapps/customer-portal/webapp/src/api/useGetCatalogItemVariables.tsapps/customer-portal/webapp/src/api/usePostCase.tsapps/customer-portal/webapp/src/api/useSearchCatalogs.tsapps/customer-portal/webapp/src/components/common/header/GetHelpDropdown.tsxapps/customer-portal/webapp/src/components/support/request-cards/RequestCard.tsxapps/customer-portal/webapp/src/components/support/request-cards/ServiceRequestCard.tsxapps/customer-portal/webapp/src/components/support/service-requests/CatalogSelector.tsxapps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestDetailContent.tsxapps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestsList.tsxapps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestsListSkeleton.tsxapps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestsSearchBar.tsxapps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestsStatCards.tsxapps/customer-portal/webapp/src/components/support/service-requests/VariableFormFields.tsxapps/customer-portal/webapp/src/constants/apiConstants.tsapps/customer-portal/webapp/src/constants/supportConstants.tsapps/customer-portal/webapp/src/models/requests.tsapps/customer-portal/webapp/src/models/responses.tsapps/customer-portal/webapp/src/pages/CreateServiceRequestPage.tsxapps/customer-portal/webapp/src/pages/ServiceRequestDetailsPage.tsxapps/customer-portal/webapp/src/pages/ServiceRequestsPage.tsxapps/customer-portal/webapp/src/utils/serviceRequestValidation.ts
…ls filtered empty
…models/responses)
…bmit when zero user variables
…lassifyServiceRequestStatus
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/customer-portal/webapp/src/api/usePostCase.ts (1)
49-61: Sanitized logging addresses prior feedback; minor redundancy in type guards.The structured logging summary properly avoids leaking sensitive data. However, the
"deploymentId" in bodyand"deployedProductId" in bodychecks on lines 52-54 are redundant since bothCreateCaseRequestandCreateServiceRequestPayloaddefine these as required fields—you can access them directly likeprojectId.♻️ Optional simplification
logger.debug("[usePostCase] Request payload summary:", { requestType: "type" in body ? body.type : "case", projectId: body.projectId, - deploymentId: "deploymentId" in body ? body.deploymentId : undefined, - deployedProductId: - "deployedProductId" in body ? body.deployedProductId : undefined, + deploymentId: body.deploymentId, + deployedProductId: body.deployedProductId, descriptionPreview: "description" in body && body.description ? `${body.description.slice(0, 80)}...`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer-portal/webapp/src/api/usePostCase.ts` around lines 49 - 61, The structured logging uses redundant type-guards for "deploymentId" and "deployedProductId"; inside usePostCase's logger.debug payload you can directly reference body.deploymentId and body.deployedProductId (remove the `"deploymentId" in body` and `"deployedProductId" in body` checks) because CreateCaseRequest and CreateServiceRequestPayload declare them as required—update the logger.debug call in usePostCase to log body.deploymentId and body.deployedProductId directly while keeping other fields unchanged.
🤖 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/support/service-requests/ServiceRequestDetailContent.tsx`:
- Around line 163-166: The comments fetch currently treats errors as empty
results; update the component that calls useGetCaseComments (the const { data:
commentsData } = useGetCaseComments(...) usage and the other instance around
lines 526-529) to read the hook's error/status flags (e.g., error, isError,
isLoading) in addition to data, and render a distinct error UI when the query
fails (use isLoading to keep loading state, render "No comments yet" only when
data is empty and there is no error). Ensure you reference the same hook return
values (error/isError/isLoading) where commentsData is used so a failed fetch
shows an explicit error state instead of "No comments yet."
- Around line 467-513: The fallback rendering can re-expose "WSO2 Product";
update ServiceRequestDetailContent to apply the same filter used for parsed
sections to the fallback text: reuse the requestDetailSections.filter((s) =>
!/^wso2\s*product$/i.test(s.label.trim())) logic (the filtered variable) and
when filtered.length === 0 or when falling back to stripHtml(data?.description),
remove any "WSO2 Product" lines from the fallback output (e.g., strip or filter
lines matching /^wso2\s*product$/i before rendering); reference
requestDetailSections, filtered, stripHtml, data?.description and the
/^wso2\s*product$/i regex so the fallback no longer shows that context.
---
Nitpick comments:
In `@apps/customer-portal/webapp/src/api/usePostCase.ts`:
- Around line 49-61: The structured logging uses redundant type-guards for
"deploymentId" and "deployedProductId"; inside usePostCase's logger.debug
payload you can directly reference body.deploymentId and body.deployedProductId
(remove the `"deploymentId" in body` and `"deployedProductId" in body` checks)
because CreateCaseRequest and CreateServiceRequestPayload declare them as
required—update the logger.debug call in usePostCase to log body.deploymentId
and body.deployedProductId directly while keeping other fields unchanged.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/customer-portal/webapp/src/api/usePostCase.tsapps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestDetailContent.tsxapps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestsSearchBar.tsxapps/customer-portal/webapp/src/constants/supportConstants.tsapps/customer-portal/webapp/src/pages/CreateServiceRequestPage.tsxapps/customer-portal/webapp/src/pages/ServiceRequestsPage.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestsSearchBar.tsx
- apps/customer-portal/webapp/src/pages/CreateServiceRequestPage.tsx
- apps/customer-portal/webapp/src/pages/ServiceRequestsPage.tsx
…quest Details fallback
…in usePostCase logging
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/support/service-requests/ServiceRequestDetailContent.tsx`:
- Around line 114-125: The flush() routine currently drops buffered text that
appears before the first <strong> label; update flush() so that if rawLabel is
empty but valueText has content, it is preserved: if sections already has at
least one entry, prepend/merge valueText into sections[0].value (e.g., join with
a newline or space) so the unlabeled intro is kept, otherwise push a new section
with label: rawLabel (empty) and value: valueText; keep the existing resets for
currentLabel and currentValueParts. Ensure you modify the flush function (and
replicate the same logic in the other similar blocks referenced) so unlabeled
intro text is not discarded.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/customer-portal/webapp/src/api/usePostCase.tsapps/customer-portal/webapp/src/components/support/service-requests/ServiceRequestDetailContent.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/customer-portal/webapp/src/api/usePostCase.ts
b72392c
into
wso2-open-operations:customer-portal-milestone-1
Purpose
Introduces the Service Requests feature in the Customer Portal so users can view, create, and manage service requests. Resolves the need for a dedicated service request workflow separate from general cases.
Goals
Approach
/support/service-requests/create@wso2/oxygen-ui-icons-react)User stories
Release note
Added Service Requests feature to the Customer Portal: list view with filters and stat cards, detail view with activity timeline and comments, and create flow with catalog-based variable forms.
Automation tests
Security checks
Learning
Summary by CodeRabbit