Skip to content

[CSM Portal] case detail: closed-case read-only, friendlier state actions, tab counts - #1079

Merged
Rashmika998 merged 5 commits into
wso2-open-operations:mainfrom
rksk:csm-case-detail-fixes
Jul 7, 2026
Merged

[CSM Portal] case detail: closed-case read-only, friendlier state actions, tab counts#1079
Rashmika998 merged 5 commits into
wso2-open-operations:mainfrom
rksk:csm-case-detail-fixes

Conversation

@rksk

@rksk rksk commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Purpose

The case detail page had several rough edges: a stale tab left open across a deploy would hard-fail with a MIME-type error instead of recovering; closed cases still accepted new comments/work notes/attachments; the "Change state" buttons read as raw backend state names instead of verbs; the More-actions menu advertised two actions with no backend flow and an unconditional auto-closure hold; and the SLA/Attachments/Time/Call-requests tabs gave no sense of how much was in each without opening it.

Goals

  • A tab that's been open across a deploy recovers automatically instead of showing a broken page.
  • Closed cases are read-only for comments, work notes, and new attachments (matching the existing time-tracking behaviour).
  • The "Change state" buttons read as actions ("Assign to me", "Start progress", "Propose solution", "Request information", "Wait on WSO2") instead of raw state names, and claiming an unassigned/other-engineer's case via "Assign to me" also starts work on it.
  • The More-actions menu drops two actions with no backend flow, routes "Request a call" to the Call requests tab's own dialog instead of a dead stub, and only offers "Hold auto-closure" while the case is actually subject to auto-closure.
  • The SLAs, Attachments, Time tracking, and Call requests tabs show their item count in the tab label.

Approach

  • main.tsx: listens for vite:preloadError and 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 shared startWork helper so both the normal transition and the new "assign then start work" path use it; gated the comment composer and attachment upload on state === "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 a TRANSITION_LABEL map for the friendlier verbs, special-cased the work_in_progress target to pick "Assign to me" vs "Start progress" (and the underlying action) based on assigneeIsMe, dropped the two unplanned menu items, and gated "Hold auto-closure" on state.
  • CallRequestsWidget.tsx: added an optional openCreateSignal prop that pops its existing create dialog when bumped externally.
  • Updated CaseActionBar.test.tsx assertions for the renamed transition labels (pre-existing, unrelated test-environment failures in that file are untouched by this change — verified they reproduce identically on main).

User stories

As a CS engineer, closing a case should stop new replies/attachments from being added to it; claiming an unassigned case should also start my work on it; and I should be able to tell at a glance how many SLAs, attachments, time entries, or call requests a case has without switching tabs.

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

  • Unit tests

    Updated the affected assertions in CaseActionBar.test.tsx for the new transition labels. Ran the full webapp suite (pnpm test); no new failures versus main (6 pre-existing failures in CaseActionBar.test.tsx reproduce identically on main, unrelated to this change).

  • Integration tests

    N/A — no integration test suite for this app.

Security checks

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, and tsc --noEmit on macOS (Node via corepack, pnpm 11.1.2).

Learning

N/A

Summary by CodeRabbit

  • New Features

    • Added a one-shot trigger to open the “Create call request” dialog from case actions.
    • Case action buttons now display clearer labels and adapt to reachable next steps.
    • Case detail tabs now show SLA and call request badge counts immediately.
  • Bug Fixes

    • Improved “start work” flow with consistent conflict handling across transitions.
    • Closed cases are now fully read-only for replying and uploading (with upload rejection).
    • Enhanced reliability after deployment chunk-load errors by reloading once per tab.
    • Improved case card/grid layout to prevent overflow.

rksk added 2 commits July 7, 2026 19:13
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)".
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 076d4750-6c40-4b8a-8b9d-623bd3e9b0b3

📥 Commits

Reviewing files that changed from the base of the PR and between e525ca9 and 25c2282.

📒 Files selected for processing (9)
  • apps/csm-portal/webapp/src/features/csm-cases/components/CallRequestsWidget.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.test.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseDetailWidgets.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseMetaBand.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
  • apps/csm-portal/webapp/src/features/csm-dashboard/api/useGetCsmDashboard.ts
  • apps/csm-portal/webapp/src/features/csm-dashboard/pages/CsmDashboardPage.tsx
  • apps/csm-portal/webapp/src/main.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/csm-portal/webapp/src/main.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.tsx

📝 Walkthrough

Walkthrough

Case 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.

Changes

Case lifecycle and case detail updates

Layer / File(s) Summary
CaseActionBar label and menu logic updates
apps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.tsx
Lifecycle transition labels are remapped, work-in-progress actions vary by assignee state, and the overflow menu changes which secondary actions are rendered or disabled.
CaseActionBar test expectations updated
apps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.test.tsx
Button label and absence assertions are updated to match the new lifecycle transition text and confirm-dialog behavior.
Case detail widget content and disabled states
apps/csm-portal/webapp/src/features/csm-cases/components/CaseDetailWidgets.tsx
Widget cards gain disabled messaging, customer and product context widgets accept project/deployment data, and several case-detail widget sections are removed or renumbered.
CaseMetaBand grid sizing update
apps/csm-portal/webapp/src/features/csm-cases/components/CaseMetaBand.tsx
Link button sizing and the overview grid template are adjusted to preserve truncation behavior inside the metadata band.
CsmCaseDetailPage lifecycle, tabs, and guards
apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
SLA and call-request data are fetched for badges, start-work conflict handling is centralized, request-call opens the call-request dialog, and closed cases disable comments and uploads.

Estimated code review effort: 4 (Complex) | ~60 minutes

Dashboard gating and reload recovery

Layer / File(s) Summary
Dashboard API gating
apps/csm-portal/webapp/src/features/csm-dashboard/api/useGetCsmDashboard.ts, apps/csm-portal/webapp/src/features/csm-dashboard/pages/CsmDashboardPage.tsx
The dashboard query hook accepts an enabled option, and the dashboard page disables the fetch while forcing the header into its fallback state.
Chunk preload/dynamic-import reload guard
apps/csm-portal/webapp/src/main.tsx
Browser listeners reload the page after Vite preload errors or dynamic-import rejections, with a sessionStorage guard to avoid repeated reloads.

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
Loading

Possibly related PRs

Suggested labels: Type/Improvement, Area/Frontend, Platform/Web, App/CSM Portal
Suggested reviewers: Rashmika998, cloby99

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main case-detail changes: read-only closed cases, friendlier state actions, and tab counts.
Description check ✅ Passed The description is mostly complete and covers purpose, goals, approach, testing, security, and release notes; a few template sections are missing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
apps/csm-portal/webapp/src/main.tsx (2)

70-74: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Broaden the unhandledrejection match
This only catches Chrome's Failed 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 value

Call event.preventDefault() in the vite:preloadError handler
Vite rethrows this preload failure unless the event is marked handled, so add event.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 win

Duplicate single-active-case conflict logic between startWork and the toggle_work_state resume branch.

Both blocks independently call findMyOngoingCases, branch on others.length === 0, and either mark workState: "ongoing" or setPauseConflict(others). Since startWork was 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 the work_in_progress state 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

📥 Commits

Reviewing files that changed from the base of the PR and between fd1bb45 and e525ca9.

📒 Files selected for processing (5)
  • apps/csm-portal/webapp/src/features/csm-cases/components/CallRequestsWidget.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.test.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActionBar.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
  • apps/csm-portal/webapp/src/main.tsx

Comment thread apps/csm-portal/webapp/src/features/csm-cases/components/CallRequestsWidget.tsx Outdated
rksk added 2 commits July 7, 2026 19:44
…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.
@rksk

rksk commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@rksk
rksk requested a review from Rashmika998 July 7, 2026 15:43
@Rashmika998
Rashmika998 merged commit 97bfa73 into wso2-open-operations:main Jul 7, 2026
1 check passed
rksk added a commit to rksk/os-cs-tools that referenced this pull request Aug 6, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants