[CSM Portal] case-type + assignee filters; align FE with BE field rename - #930
Conversation
|
Warning Review limit reached
More reviews will be available in 53 minutes and 52 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?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 credits. 🚦 How do rate 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 see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughFrontend types, hooks, and components in the CSM portal are updated to match backend API field renames (dropping ChangesBackend Alignment, Case-Type Filtering, and Assignee Email Migration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ 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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/csm-portal/webapp/src/features/csm-cases/api/useFindMyOngoingCases.ts (1)
56-65: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftPage through the search results before concluding there are no ongoing cases.
Now that this hook always uses
/cases/search, it only inspects the first 50work_in_progressrows and then filters assignee/workState client-side. If there are more than 50 matching cases overall, this can miss the current user's ongoing case and suppress the warning flow.Suggested fix
- const res = await api.post<BeCaseSearchPayload, BeCaseSearchResponse>( - "/cases/search", - { - filters: { stateKeys: ["work_in_progress"] }, - pagination: { offset: 0, limit: SEARCH_LIMIT }, - }, - ); - - return (res.cases ?? []) - .filter( - (c) => - c.id !== excludeCaseId && - // A null/absent workState is never "ongoing". - c.workState === "ongoing" && - c.assignedEngineer?.email?.toLowerCase() === myEmail, - ) - .map((c) => ({ - id: c.id, - label: c.internalId || c.number || c.title || c.id, - })); + const matches: MyOngoingCase[] = []; + + for (let offset = 0; ; offset += SEARCH_LIMIT) { + const res = await api.post<BeCaseSearchPayload, BeCaseSearchResponse>( + "/cases/search", + { + filters: { stateKeys: ["work_in_progress"] }, + pagination: { offset, limit: SEARCH_LIMIT }, + }, + ); + + const page = res.cases ?? []; + matches.push( + ...page + .filter( + (c) => + c.id !== excludeCaseId && + c.workState === "ongoing" && + c.assignedEngineer?.email?.toLowerCase() === myEmail, + ) + .map((c) => ({ + id: c.id, + label: c.internalId || c.number || c.title || c.id, + })), + ); + + if (page.length < SEARCH_LIMIT) break; + } + + return matches;🤖 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/csm-portal/webapp/src/features/csm-cases/api/useFindMyOngoingCases.ts` around lines 56 - 65, The `useFindMyOngoingCases` search currently only checks the first page of `/cases/search` results, so it can miss the user’s case when there are more than 50 `work_in_progress` items. Update the logic in the `useFindMyOngoingCases` request flow to page through `api.post("/cases/search", ...)` results until all matching cases are exhausted, then apply the existing assignee/workState filtering before deciding there are no ongoing cases.apps/csm-portal/webapp/src/api/useDirectoryUsers.ts (1)
61-64: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winFilter out directory users without emails.
/users/searchcan return users with noassignedTo. Returning name-only users can create empty/invalid assignee filter values.Proposed fix
- return rows.map(toDirectoryUser).filter((u) => u.name); + return rows.map(toDirectoryUser).filter((u) => u.name && u.email);🤖 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/csm-portal/webapp/src/api/useDirectoryUsers.ts` around lines 61 - 64, The user list normalization in useDirectoryUsers should exclude entries that do not have an email address, since assignee options are email-backed and invalid name-only users can leak through. Update the mapping/filtering around toDirectoryUser so the returned rows only include users with a valid email, while preserving the existing name check and the /users/search response handling.
🧹 Nitpick comments (2)
apps/csm-portal/webapp/src/features/csm-dashboard/api/useCaseComposition.ts (1)
79-133: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy liftAvoid recomputing counts the matrix already fetched.
CsmDashboardPagerendersCaseCountsMatrixandCaseCompositionChartstogether, so this hook issues a second wave of/cases/searchrequests for the same active severity/state totals that the matrix query already loaded. DerivingbySeverity/byStatefrom the matrix data (or moving both widgets onto one aggregate backend response) would cut dashboard load/refetch traffic substantially while keeping only the separate closed-case lookup here.🤖 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/csm-portal/webapp/src/features/csm-dashboard/api/useCaseComposition.ts` around lines 79 - 133, The hook in useCaseComposition is recomputing active severity/state totals that CaseCountsMatrix already fetches, causing duplicate /cases/search traffic. Reuse the matrix-provided counts to populate bySeverity and byState instead of issuing the Promise.all wave in this hook, and keep only the separate closed-case lookup here. If needed, adjust the data flow between CsmDashboardPage, CaseCountsMatrix, and CaseCompositionCharts so both widgets consume a shared aggregate response.apps/csm-portal/webapp/src/features/csm-cases/components/CasesFilterBar.tsx (1)
80-99: 📐 Maintainability & Code Quality | 🔵 TrivialMove
ASSIGNEE_ME_TOKENandCasesFiltersinto a shared module. They’re used by the URL helpers and cases API, so keeping them inCasesFilterBar.tsxmixes shared contract data with the component.🤖 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/csm-portal/webapp/src/features/csm-cases/components/CasesFilterBar.tsx` around lines 80 - 99, Move the shared filter contract out of CasesFilterBar.tsx: the ASSIGNEE_ME_TOKEN constant and CasesFilters interface are used by the URL helpers and cases API, so extract them into a dedicated shared module and update all imports to reference that module. Keep CasesFilterBar focused on UI state, and make sure the shared symbol names remain ASSIGNEE_ME_TOKEN and CasesFilters so existing callers can be migrated cleanly.
🤖 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/csm-portal/webapp/src/features/csm-cases/utils/casesFiltersUrl.ts`:
- Line 74: The assignee query parsing in casesFiltersUrl currently forwards
arbitrary values from parseFreeFormCsv(params.get("assignees")) into the backend
search filter. Update the assignees handling so only valid values are kept:
allow the special token `@me` and email-shaped entries, and discard anything else
before building the backend payload. Keep the fix localized to the assignees
mapping in the URL-to-filter conversion logic so the rest of the filter parsing
remains unchanged.
---
Outside diff comments:
In `@apps/csm-portal/webapp/src/api/useDirectoryUsers.ts`:
- Around line 61-64: The user list normalization in useDirectoryUsers should
exclude entries that do not have an email address, since assignee options are
email-backed and invalid name-only users can leak through. Update the
mapping/filtering around toDirectoryUser so the returned rows only include users
with a valid email, while preserving the existing name check and the
/users/search response handling.
In `@apps/csm-portal/webapp/src/features/csm-cases/api/useFindMyOngoingCases.ts`:
- Around line 56-65: The `useFindMyOngoingCases` search currently only checks
the first page of `/cases/search` results, so it can miss the user’s case when
there are more than 50 `work_in_progress` items. Update the logic in the
`useFindMyOngoingCases` request flow to page through `api.post("/cases/search",
...)` results until all matching cases are exhausted, then apply the existing
assignee/workState filtering before deciding there are no ongoing cases.
---
Nitpick comments:
In `@apps/csm-portal/webapp/src/features/csm-cases/components/CasesFilterBar.tsx`:
- Around line 80-99: Move the shared filter contract out of CasesFilterBar.tsx:
the ASSIGNEE_ME_TOKEN constant and CasesFilters interface are used by the URL
helpers and cases API, so extract them into a dedicated shared module and update
all imports to reference that module. Keep CasesFilterBar focused on UI state,
and make sure the shared symbol names remain ASSIGNEE_ME_TOKEN and CasesFilters
so existing callers can be migrated cleanly.
In `@apps/csm-portal/webapp/src/features/csm-dashboard/api/useCaseComposition.ts`:
- Around line 79-133: The hook in useCaseComposition is recomputing active
severity/state totals that CaseCountsMatrix already fetches, causing duplicate
/cases/search traffic. Reuse the matrix-provided counts to populate bySeverity
and byState instead of issuing the Promise.all wave in this hook, and keep only
the separate closed-case lookup here. If needed, adjust the data flow between
CsmDashboardPage, CaseCountsMatrix, and CaseCompositionCharts so both widgets
consume a shared aggregate response.
🪄 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
Run ID: 99b236bd-9d8b-4508-9937-54e02b18d14a
📒 Files selected for processing (35)
apps/csm-portal/webapp/src/AppWithConfig.tsxapps/csm-portal/webapp/src/api/backend/client.tsapps/csm-portal/webapp/src/api/backend/types.tsapps/csm-portal/webapp/src/api/useDirectoryUsers.tsapps/csm-portal/webapp/src/components/header/Actions.tsxapps/csm-portal/webapp/src/components/header/MockModeToggle.tsxapps/csm-portal/webapp/src/config/authConfig.tsapps/csm-portal/webapp/src/context/mock-mode/MockModeContext.tsxapps/csm-portal/webapp/src/features/csm-cases/api/mocks/casesMocks.tsapps/csm-portal/webapp/src/features/csm-cases/api/mocks/commentsMocks.tsapps/csm-portal/webapp/src/features/csm-cases/api/useCsmCaseAttachments.tsapps/csm-portal/webapp/src/features/csm-cases/api/useCsmCaseComments.tsapps/csm-portal/webapp/src/features/csm-cases/api/useFindMyOngoingCases.tsapps/csm-portal/webapp/src/features/csm-cases/api/useGetCsmCaseDetail.tsapps/csm-portal/webapp/src/features/csm-cases/api/useGetCsmCases.tsapps/csm-portal/webapp/src/features/csm-cases/api/usePatchCsmCase.tsapps/csm-portal/webapp/src/features/csm-cases/api/usePostCsmCase.tsapps/csm-portal/webapp/src/features/csm-cases/api/useQuickCaseSearch.tsapps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.test.tsxapps/csm-portal/webapp/src/features/csm-cases/components/CasesFilterBar.tsxapps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsxapps/csm-portal/webapp/src/features/csm-cases/pages/CsmCasesPage.tsxapps/csm-portal/webapp/src/features/csm-cases/types/csmCases.tsapps/csm-portal/webapp/src/features/csm-cases/utils/caseType.tsapps/csm-portal/webapp/src/features/csm-cases/utils/casesClientFilter.tsapps/csm-portal/webapp/src/features/csm-cases/utils/casesFiltersUrl.test.tsapps/csm-portal/webapp/src/features/csm-cases/utils/casesFiltersUrl.tsapps/csm-portal/webapp/src/features/csm-dashboard/api/mocks/dashboardMocks.tsapps/csm-portal/webapp/src/features/csm-dashboard/api/useCaseComposition.tsapps/csm-portal/webapp/src/features/csm-dashboard/api/useCaseCountsMatrix.tsapps/csm-portal/webapp/src/features/csm-dashboard/api/useGetCsmDashboard.tsapps/csm-portal/webapp/src/features/csm-dashboard/components/MyQueueSection.tsxapps/csm-portal/webapp/src/features/csm-dashboard/components/SlaAtRiskSection.tsxapps/csm-portal/webapp/src/main.tsxapps/csm-portal/webapp/vite.config.ts
💤 Files with no reviewable changes (12)
- apps/csm-portal/webapp/src/features/csm-dashboard/components/MyQueueSection.tsx
- apps/csm-portal/webapp/src/main.tsx
- apps/csm-portal/webapp/src/features/csm-cases/utils/casesClientFilter.ts
- apps/csm-portal/webapp/src/context/mock-mode/MockModeContext.tsx
- apps/csm-portal/webapp/src/features/csm-cases/api/mocks/commentsMocks.ts
- apps/csm-portal/webapp/src/features/csm-cases/api/mocks/casesMocks.ts
- apps/csm-portal/webapp/src/api/backend/client.ts
- apps/csm-portal/webapp/src/components/header/MockModeToggle.tsx
- apps/csm-portal/webapp/src/features/csm-dashboard/api/mocks/dashboardMocks.ts
- apps/csm-portal/webapp/src/features/csm-dashboard/components/SlaAtRiskSection.tsx
- apps/csm-portal/webapp/src/components/header/Actions.tsx
- apps/csm-portal/webapp/src/config/authConfig.ts
|
@coderabbitai review |
✅ Action performedReview finished.
|
Add two filters to the cases list filter bar: - Case type: a multi-select dropdown (Support, Service request, Security report, Announcement, Engagement) wired to the /cases/search 'typeKeys' filter and serialized to the 'types' URL param. - Assignee: an email-based engineer picker (options labelled by name, valued by email) plus a '@me' sentinel. Wired to /cases/search as 'assignedTo' (engineer emails) + 'assignedToMe' (caller), mirroring the existing createdBy / createdByMe pair. NOTE: these search-filter fields are pending backend support; the FE sends them so the filter works the moment the entity/BFF add them. Serialized to the 'assignees' URL param. Both counted as active filters and round-tripped through the URL.
…itle->subject)
Backend dropped the Key/Keys suffix from case enum request fields and
standardised the search response on 'subject'. Align every FE call site:
- /cases/search filters: severityKeys->severities, stateKeys->states,
typeKeys->types (useGetCsmCases, useFindMyOngoingCases,
useCaseCountsMatrix, useCaseComposition; BeCaseSearchFilters type).
- search response: title->subject (the list subject was rendering as
'(no subject)'), and caseType dropped from the search view
(useGetCsmCases / useQuickCaseSearch / useFindMyOngoingCases mappers,
BeCaseSearchView type).
- POST /cases: typeKey->type, severityKey->severity, issueTypeKey->issueType
(CsmCaseCreatePage, BeCaseCreatePayload).
- PATCH /cases/{id}: stateKey->state, severityKey->severity,
workStateKey->workState (CsmCaseDetailPage, BeCaseUpdatePayload).
Pure field renames; no behaviour change. assignedTo/assignedToMe (the
pending assignee filter) left intact.
|
@coderabbitai review |
✅ Action performedReview finished.
|
…g-case lookup, require directory email
- Assignee filter: disabled in the UI ('coming soon') and NOT sent in the
/cases/search payload. /cases/search has no assigned-engineer filter and
the entity rejects unknown fields, so sending one would 400 the whole
search. The email-based picker stays, ready to enable when the BE adds
the field. Removes the speculative assignedTo/assignedToMe.
- useFindMyOngoingCases: page through all work_in_progress results (bounded)
instead of inspecting only the first page, so the single-active-case guard
can't miss a case beyond page 1 (CodeRabbit, Major).
- useDirectoryUsers: drop directory users without an email, since assignee
options are email-backed (CodeRabbit, Major).
|
Thanks @coderabbitai — dispositions: Fixed
No longer applicable
Deferred (pre-existing, out of this PR's scope)
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Tip For best results, initiate chat on the files or code changes.
Fixes ✓ — Pagination guard in No longer applicable ✓ — Makes complete sense. Validating/sanitising Deferred — Good call keeping the dashboard duplicate-traffic concern out of scope here. Would you like me to open a GitHub issue to track the 🐇 (__/) ✏️ Learnings added
If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
useFindMyOngoingCases paged at 100, which exceeds BE_MAX_PAGE_LIMIT (50) and would be rejected by the backend. Use BE_MAX_PAGE_LIMIT as the page size. All other search requests already sit at or below the cap.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx`:
- Around line 584-586: The work-state transition in CsmCaseDetailPage’s case
update flow is not atomic, so a successful pause on older cases can be left
behind if a later patchCaseById or patchCase.mutateAsync call fails. Update this
flow so the grouped case-state change is handled atomically on the server, or
add compensating logic that restores any already-paused cases before
rethrowing/surfacing the error from the patchCaseById and patchCase steps.
🪄 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
Run ID: 31498ab6-3ddf-41da-a104-cff7210c1ace
📒 Files selected for processing (14)
apps/csm-portal/webapp/src/api/backend/types.tsapps/csm-portal/webapp/src/api/useDirectoryUsers.tsapps/csm-portal/webapp/src/features/csm-cases/api/useFindMyOngoingCases.tsapps/csm-portal/webapp/src/features/csm-cases/api/useGetCsmCases.tsapps/csm-portal/webapp/src/features/csm-cases/api/useQuickCaseSearch.tsapps/csm-portal/webapp/src/features/csm-cases/components/CasesFilterBar.tsxapps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseCreatePage.tsxapps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsxapps/csm-portal/webapp/src/features/csm-cases/types/csmCases.tsapps/csm-portal/webapp/src/features/csm-cases/utils/caseType.tsapps/csm-portal/webapp/src/features/csm-cases/utils/casesFiltersUrl.test.tsapps/csm-portal/webapp/src/features/csm-cases/utils/casesFiltersUrl.tsapps/csm-portal/webapp/src/features/csm-dashboard/api/useCaseComposition.tsapps/csm-portal/webapp/src/features/csm-dashboard/api/useCaseCountsMatrix.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/csm-portal/webapp/src/features/csm-cases/utils/caseType.ts
- apps/csm-portal/webapp/src/features/csm-cases/utils/casesFiltersUrl.ts
- apps/csm-portal/webapp/src/features/csm-cases/types/csmCases.ts
- apps/csm-portal/webapp/src/features/csm-cases/components/CasesFilterBar.tsx
Stacked on the merged mock-removal work; rebased onto current
v2.1. Case-type + assignee filters
/cases/searchtypes, URL paramtypes.@me. Disabled ("coming soon") and not sent (no/cases/searchassignee filter yet), so it can't break the search. Built to enable in one flip once the BE adds it.2. Align FE with the BE Key/Keys rename (entity PR #929 / BFF #933)
severityKeys→severities,stateKeys→states,typeKeys→types; responsetitle→subject;POST /casestypeKey→type,severityKey→severity,issueTypeKey→issueType;PATCH /cases/{id}stateKey→state/severityKey→severity/workStateKey→workState; deployment searchdeploymentTypeKeys→deploymentTypes.3. Review fixes
useFindMyOngoingCasespages through allwork_in_progressresults (bounded byBE_MAX_PAGE_LIMIT).useDirectoryUsersdrops directory entries without an email.4. Align FE with BE PR #938 (entity #934)
support→caseacrossBeCaseType,POST /cases, the case-type filter (label "Support" → "Case"), andCsmCaseCreatePage.CaseView: add nullabletype/engagementType+catalog/catalogItem/assignedTeam/conversationrefs; markdeployment/deployedProductnullable.CaseSearchView: addtype(mapped onto the row); markdeployment/deployedProductnullable.Testing
pnpm lint,pnpm test,pnpm build,tsc -ball green.