[CSM Portal] case detail: closed-case read-only, friendlier state actions, tab counts - #1079
Conversation
A tab left open across a deploy still holds the old build's chunk hashes; the next lazy-loaded route 404s to the SPA fallback (HTML instead of JS), which the browser rejects as a MIME mismatch. Reload once (session-guarded) on vite:preloadError / a failed dynamic import so the tab picks up the new build instead of erroring out.
…ions, tab counts - Closed cases are read-only for comments/work notes and new attachments, matching the existing time-tracking gate. - "Change state" transition buttons read as verbs instead of raw state names: "Assign to me" / "Start progress" (depending on current assignee) for Work in progress, "Propose solution", "Request information", "Wait on WSO2". Clicking "Assign to me" claims the case before starting work, instead of only moving the state. - More-actions menu: drop "Escalate to lead" and "Request severity change" (no backend flow), route "Request a call" to the Call requests tab's own create dialog, and only show "Hold auto-closure" while the case is awaiting info or has a solution proposed. - SLAs, Attachments, Time tracking, and Call requests tabs show their item count in the tab label, e.g. "SLAs (3)".
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughCase action and detail flows are updated for new transition labels, call-request opening, and closed-case guards. Dashboard fetching is gated behind an enabled flag. The app entry adds a browser reload recovery path for failed dynamic imports. ChangesCase lifecycle and case detail updates
Estimated code review effort: 4 (Complex) | ~60 minutes Dashboard gating and reload recovery
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant CsmCaseDetailPage
participant BackendAPI
participant CallRequestsWidget
User->>CsmCaseDetailPage: trigger work_in_progress or request_call
CsmCaseDetailPage->>BackendAPI: fetch ongoing cases / patch assigneeEmail
CsmCaseDetailPage->>BackendAPI: patch case to work_in_progress
CsmCaseDetailPage->>CsmCaseDetailPage: resolve conflict or mark ongoing
CsmCaseDetailPage->>CallRequestsWidget: set autoOpenCreate
CallRequestsWidget->>User: open create call request dialog
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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 |
…ount The "Create call request" dialog was gated on a signal prop that was always defined (started at 0), so simply clicking into the Call requests tab remounted the widget and popped the dialog even without ever using "Request a call". Switched to a one-shot boolean that the widget clears after acting on it, so only an explicit "Request a call" click opens it.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
apps/csm-portal/webapp/src/main.tsx (2)
70-74: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winBroaden the
unhandledrejectionmatch
This only catches Chrome'sFailed to fetch dynamically imported module; Firefox and Safari use different rejection text, so the reload fallback misses those browsers. Match the other strings too.🤖 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/main.tsx` around lines 70 - 74, The unhandled rejection handler in main should match more than Chrome’s dynamic import failure text so the reload fallback works in Firefox and Safari too. Update the `window.addEventListener("unhandledrejection", ...)` logic near `reloadForNewBuild()` to recognize the other browser-specific rejection messages for failed dynamic imports, while keeping the existing Chrome check.
69-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCall
event.preventDefault()in thevite:preloadErrorhandler
Vite rethrows this preload failure unless the event is marked handled, so addevent.preventDefault()here to avoid the extra uncaught error before the reload runs.🤖 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/main.tsx` at line 69, The vite:preloadError handler in main.tsx should mark the event as handled before triggering the reload. Update reloadForNewBuild to accept the event and call event.preventDefault() inside that handler so Vite does not rethrow the preload failure before the page reloads.apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx (1)
430-484: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate single-active-case conflict logic between
startWorkand thetoggle_work_stateresume branch.Both blocks independently call
findMyOngoingCases, branch onothers.length === 0, and either markworkState: "ongoing"orsetPauseConflict(others). SincestartWorkwas introduced specifically to unify this flow, consider extracting the "look up other ongoing cases → mark ongoing or prompt conflict" portion into a shared helper (separate from thework_in_progressstate transition) so both the start-work and resume-work paths call it.Also applies to: 591-624
🤖 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/pages/CsmCaseDetailPage.tsx` around lines 430 - 484, The single-active-case conflict handling is duplicated between `startWork` and the `toggle_work_state` resume path, both repeating the `findMyOngoingCases` check plus the `others.length === 0` branch that either sets `workState: "ongoing"` or calls `setPauseConflict`. Extract that shared “check for other ongoing cases and resolve/prompt” logic into a helper near `startWork`, and have both the start-work flow and the resume branch call it after their respective state transitions so the `work_in_progress` update stays separate from the conflict resolution.
🤖 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/components/CallRequestsWidget.tsx`:
- Around line 60-62: The create-dialog trigger signal is staying truthy after
being consumed, which causes CallRequestsWidget to auto-open on mount and on
later remounts. Update the state that drives openCreateSignal so it starts as
undefined instead of 0, and in the parent component clear the signal immediately
after CallRequestsWidget reacts to it. Make sure the handling around
CallRequestsWidget and the state that sets openCreateSignal both reset the value
after opening the dialog.
In `@apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx`:
- Around line 500-521: Add an explicit guard in CsmCaseDetailPage’s state-change
handler before the generic targetState === "work_in_progress" path so
“assign_to_me” cannot fall through when currentUserEmail is missing. In the
assign_to_me branch, if currentUserEmail is falsy, show an error and return
instead of calling startWork; otherwise keep the existing patchCase.mutate flow
and only invoke startWork from onSuccess after assigneeEmail is updated.
---
Nitpick comments:
In `@apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx`:
- Around line 430-484: The single-active-case conflict handling is duplicated
between `startWork` and the `toggle_work_state` resume path, both repeating the
`findMyOngoingCases` check plus the `others.length === 0` branch that either
sets `workState: "ongoing"` or calls `setPauseConflict`. Extract that shared
“check for other ongoing cases and resolve/prompt” logic into a helper near
`startWork`, and have both the start-work flow and the resume branch call it
after their respective state transitions so the `work_in_progress` update stays
separate from the conflict resolution.
In `@apps/csm-portal/webapp/src/main.tsx`:
- Around line 70-74: The unhandled rejection handler in main should match more
than Chrome’s dynamic import failure text so the reload fallback works in
Firefox and Safari too. Update the
`window.addEventListener("unhandledrejection", ...)` logic near
`reloadForNewBuild()` to recognize the other browser-specific rejection messages
for failed dynamic imports, while keeping the existing Chrome check.
- Line 69: The vite:preloadError handler in main.tsx should mark the event as
handled before triggering the reload. Update reloadForNewBuild to accept the
event and call event.preventDefault() inside that handler so Vite does not
rethrow the preload failure before the page reloads.
🪄 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: 8cd12122-4f27-4950-90a8-c7df704976d2
📒 Files selected for processing (5)
apps/csm-portal/webapp/src/features/csm-cases/components/CallRequestsWidget.tsxapps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.test.tsxapps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.tsxapps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsxapps/csm-portal/webapp/src/main.tsx
…p, live deployment/account data Review feedback (PR wso2-open-operations#1079): - Broaden the chunk-reload unhandledrejection match to Firefox/Safari wording and call preventDefault() in the vite:preloadError handler. - Guard "assign to me" so a missing signed-in email surfaces an error instead of silently starting work without ever assigning the case. - Extract the duplicated single-active-case conflict logic (startWork / resume-work) into a shared resolveOngoingConflict helper. Dashboard: - Stop calling GET /csm/dashboard — csm-portal-backend has no route for it (confirmed against cmd/server/main.go), so it always 404s. Gated behind the same "not implemented yet" pattern already used for ABT scoping/the dashboard switcher; the header's existing fallback still renders. Case detail — Details tab: - Removed "Assignment group (ABT)" from Identifiers & timestamps. - "Deployment info" now looks up the live deployment (POST /deployments/search by project, matched on deploymentId) for its name/type, instead of only the snapshot embedded in the case-detail payload. - Customer card drops the "Open cases" count and adds subscription type/period, project key, and account-since date via GET /projects/{id}. - Watchers and Linked items are now shown disabled (tooltip explaining why) rather than offering an add/link action with no backing flow. Case detail — overview band: - Fixed the Deployment value overlapping into the Product cell: a <button> (LinkButton) doesn't shrink like an <a> as a grid item without an explicit width/minWidth, and the xs grid template used a plain 1fr track with no minmax(0, ...) floor. Case detail — action bar: - A single reachable next state now renders as one direct button instead of a "Change state" menu with one item to pick.
…p Watchers/Linked items, "Close" label Details tab: - "Deployment info": type is a plain row instead of a chip; dropped the Version row since the product display name already includes the version. - "Customer": dropped Primary contact; now shows Account Manager, Technical Owner, Region, Project name, Project key, Subscription type, and Subscription period (no subscription-status field exists on the project record today, so that one's omitted rather than inferred). - Removed the Watchers and Linked items widgets entirely (not just disabled) along with their now-dead secondary actions and unused imports. Action bar: - The "Change state" transition into Closed now reads "Close" instead of "Closed", matching the other transition verbs.
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
hidden: true was reintroduced in wso2-open-operations#1267 with no recorded rationale, three weeks after the tab shipped visible in wso2-open-operations#1049/wso2-open-operations#1079. The underlying data path (useGetCsmCaseSlas -> BFF/entity-service POST /slas/search) works and is already being fetched on page load; only the tab button was unreachable.
Purpose
Goals
Approach
main.tsx: listens forvite:preloadErrorand for the "Failed to fetch dynamically imported module" rejection, and reloads the page once (session-guarded so a genuinely broken deploy doesn't loop).CsmCaseDetailPage.tsx: extracted the existing "start work" flow (single-active-case check + PATCH) into a sharedstartWorkhelper so both the normal transition and the new "assign then start work" path use it; gated the comment composer and attachment upload onstate === "closed"; added page-level SLA/call-request count queries feeding the tab labels; wired "Request a call" to switch tabs and pop the Call requests create dialog via a small open-signal counter prop.CaseActionBar.tsx: added aTRANSITION_LABELmap for the friendlier verbs, special-cased thework_in_progresstarget to pick "Assign to me" vs "Start progress" (and the underlying action) based onassigneeIsMe, dropped the two unplanned menu items, and gated "Hold auto-closure" on state.CallRequestsWidget.tsx: added an optionalopenCreateSignalprop that pops its existing create dialog when bumped externally.CaseActionBar.test.tsxassertions for the renamed transition labels (pre-existing, unrelated test-environment failures in that file are untouched by this change — verified they reproduce identically onmain).User stories
Release note
Case detail page: closed cases are now read-only for comments/attachments, the state-change buttons read as actions instead of raw state names, the More-actions menu was trimmed to actions that actually work, and the SLA/Attachments/Time tracking/Call requests tabs show their item counts.
Documentation
N/A — internal case-detail UX change, no external-facing docs.
Automation tests
Security checks
pnpm lintandtsc --noEmitclean instead)Samples
N/A
Related PRs
None
Migrations (if applicable)
N/A — no data model changes.
Test environment
Verified with
pnpm build,pnpm lint,pnpm test, andtsc --noEmiton macOS (Node via corepack, pnpm 11.1.2).Learning
N/A
Summary by CodeRabbit
New Features
Bug Fixes