[CSM Portal] Inline resume-work quick-fix for the locked public-reply toggle - #1369
Conversation
Relayed from the team: when a case's public-comment toggle is disabled because work is only paused (not because the case hasn't started), the composer now says so next to Internal note and offers a one-click "Resume work" fix, instead of just a disabled toggle with no path forward. Scoped to that one lock reason specifically — the case-not-started reason still needs the full assign/start flow, which doesn't belong inline here, so it keeps its plain-text explanation with no link. Resuming reuses the same PATCH + single-active-case conflict check the case header's own Resume control already runs (the parent wires this through, not reimplemented here) — once it succeeds, the lock clears on its own via the normal data refetch and the toggle unlocks; this doesn't flip the toggle or send anything by itself. Also folds the reason text into one place instead of two: previously the raw lock reason repeated in the send-row status line even when the new quick-fix above it was already explaining the same thing. That line now only shows the reason when there's no quick-fix covering it.
… page
Computes canResumeToUnlockPublicReply (case is work_in_progress and
assigned, just not ongoing) alongside the existing publicReplyGateReason,
and passes it plus onResumeWork/isResumingWork to CsmCaseCommentInput.
onResumeWork reuses onAction({ secondary: "toggle_work_state" }) directly —
the same handler the case header's own Resume action already calls — so
the single-active-case conflict check isn't duplicated.
First test file for this component. Covers: the link shows and calls onResumeWork when resuming would unlock public replies; it's absent when the case hasn't started yet (the other lock reason, with no one-click fix); it's absent when public replies are already allowed; it disables and relabels itself while a resume is in flight; and the raw lock reason no longer also appears in the send-row status line once the quick-fix above it is already showing it. Mocks @api/backend/client (pulled in transitively via CsmUploadAttachmentModal -> useCsmCaseAttachments, whose real client reads runtime config at module load — same approach as CaseActivitiesFeed.test.tsx) and stubs the rich-text Editor to a plain textarea, matching EditCaseDetailsDialog.test.tsx's precedent for the same dependency.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe public reply composer now shows a resume-work action for eligible paused cases. The case detail page computes eligibility, invokes work-state handling, and passes pending state. Case lists and previews normalize missing work states to ChangesPublic reply resume flow
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant CsmCaseDetailPage
participant CsmCaseCommentInput
participant WorkStateAction
CsmCaseDetailPage->>CsmCaseCommentInput: Pass eligibility and pending state
CsmCaseCommentInput->>CsmCaseDetailPage: Invoke onResumeWork
CsmCaseDetailPage->>WorkStateAction: Toggle work state
WorkStateAction-->>CsmCaseDetailPage: Update patch pending state
WorkStateAction-->>CsmCaseDetailPage: Update effective work state
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/CsmCaseCommentInput.test.tsx`:
- Around line 21-28: Add a top-level mock for `@config/apiConfig` before importing
CsmCaseCommentInput or any transitive dependencies, exporting
apiConfig.backendUrl as "https://example.test". Keep the existing
`@api/backend/client` mock and ensure both mocks are declared before component
imports.
In `@apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx`:
- Around line 1590-1591: Update canResumeToUnlockPublicReply in
CsmCaseDetailPage so it also requires the existing page condition indicating the
case is assigned to the current engineer, while preserving the work_in_progress
and non-ongoing checks. Add coverage confirming the capability is false for
unassigned and other-engineer paused cases.
🪄 Autofix
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 Plus
Run ID: 953e90d4-36ba-48f9-aed4-64649b205335
📒 Files selected for processing (3)
apps/csm-portal/webapp/src/features/csm-cases/components/CsmCaseCommentInput.test.tsxapps/csm-portal/webapp/src/features/csm-cases/components/CsmCaseCommentInput.tsxapps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
…x on assignee Confirmed against CaseActionBar.tsx: the "Resume work" action it exposes is already gated on caseDetail.assigneeIsMe && state === "work_in_progress" — pause/resume is only ever offered to the case's own assignee. The comment composer's quick-fix (added in the previous commits on this branch) didn't carry that same check, so an engineer viewing someone else's paused case would see a "Resume work" link that isn't actually theirs to use. Extracted the condition into canResumeToUnlockPublicReply(state, workState, assigneeIsMe) in caseWorkState.ts, alongside the existing caseAcceptsPublicComments/publicCommentGateReason — same module, same pattern, and (unlike the page component, which has no test harness) it's a plain function unit tests can hit directly. Added the exact coverage requested: false for an unassigned/other-engineer paused case, alongside the already-true/already-false cases. Also verified the second finding (CsmCaseCommentInput.test.tsx needs an @config/apiConfig mock) against current code and skipped it: grepped the component's full import chain (CsmUploadAttachmentModal -> useCsmCaseAttachments -> @api/backend/client, already mocked; the rich-text Editor stub) and nothing in it imports @config/apiConfig directly, so there's nothing for that mock to intercept. The existing test suite (17 tests across both files) already passes without it.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/caseWorkState.test.ts`:
- Around line 73-97: Add regression assertions in the
canResumeToUnlockPublicReply test suite for a "work_in_progress" case with both
null and undefined work states, and expect false for each. Keep the existing
paused, ongoing, and open-case coverage unchanged.
In `@apps/csm-portal/webapp/src/features/csm-cases/utils/caseWorkState.ts`:
- Around line 56-78: Update canResumeToUnlockPublicReply to require workState
=== "paused" instead of accepting every non-"ongoing" value. Preserve the
existing assigneeIsMe and state === "work_in_progress" checks so the resume
quick-fix is offered only for explicitly paused cases.
🪄 Autofix
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 Plus
Run ID: 1807cf55-03ff-4371-b3e9-89253c8eea6e
📒 Files selected for processing (3)
apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsxapps/csm-portal/webapp/src/features/csm-cases/utils/caseWorkState.test.tsapps/csm-portal/webapp/src/features/csm-cases/utils/caseWorkState.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
The work-state chip (cases list, case header, quick-preview drawer) only rendered when workState was truthy — a work_in_progress case whose work state was never explicitly set (null) showed no chip at all, reading as "no status" when it's actually not ongoing, i.e. effectively paused. canResumeToUnlockPublicReply already treats a null workState the same as paused for behavior (offering the resume quick-fix); the chip now matches that for display. Added effectiveWorkState(workState) to caseWorkState.ts (workState ?? "paused") as the one place this null-handling rule lives, and used it at all three render sites instead of each repeating its own workState && truthy check.
…cReply Verified against CaseActionBar.tsx and rejected the proposed code change (require workState === "paused" exactly): its buildSecondaryItems has its own explicit, deliberate comment establishing that a null work-state work_in_progress case is resumable too — "anything else (paused OR a null work-state in-progress case) is resumable — otherwise the only action that can set `ongoing` would be hidden for null work-state cases." Narrowing canResumeToUnlockPublicReply to `=== "paused"` would make the comment composer's quick-fix disagree with the action bar's own "Resume work" item for the exact same case. Kept the valid part: added the missing regression coverage for a work_in_progress case with a null/undefined work state, asserting `true` (the correct, already-shipped behavior) rather than CodeRabbit's proposed `false`. Also expanded the function's own doc comment to spell out why `!== "ongoing"` is deliberate, so this doesn't get re-flagged the same way next time.
|
@coderabbitai review |
✅ Action performedReview finished.
|
Purpose
The reply composer already disables the public-comment toggle when the case isn't in a state
that accepts customer-visible replies, but gives no way to act on it — an engineer with a paused
case just sees a disabled switch and has to leave the composer, find the "Resume work" control
elsewhere on the page, then come back.
Goals
When the specific reason a public reply is locked is paused work (not "the case hasn't started
at all"), let the engineer resume and unlock it from right where they're already typing, instead
of hunting for the fix elsewhere.
Approach
CsmCaseCommentInputgets three new optional props:canResumeToUnlockPublicReply,onResumeWork,isResumingWork. When the reply is locked and resuming would unlock it, a lineappears next to the Internal note toggle: "Only resumed work can send public replies to the
customer. Resume work to publish this as a public comment." — with "Resume work" as a real
button styled as a link.
Scoped deliberately to just that one lock reason. The other one (case not started yet) needs the
full assign/start flow with its own single-active-case conflict dialog, which doesn't belong
squeezed into a reply box — it keeps its existing plain-text explanation with no link.
CsmCaseDetailPagecomputescanResumeToUnlockPublicReply(case iswork_in_progressandalready assigned, just not
ongoing) and wiresonResumeWorkstraight to the existingonAction({ secondary: "toggle_work_state" })handler — the same one the case header's ownResume control calls, including its single-active-case conflict check. Nothing about that flow
is reimplemented in the composer.
Also merges what became two copies of the same message once the quick-fix landed: the send-row
status line used to always echo the raw lock reason, which would have doubled up with the new
line above it. It now only shows the reason when there's no quick-fix already covering it,
falling back to the normal "Ctrl/Cmd + Enter to send." hint otherwise.
Resuming only resumes — it never flips the Internal note toggle or sends anything on its own.
Once it succeeds, the case-detail query refetches, the lock clears, and the toggle unlocks; the
engineer still explicitly toggles to public and hits Send themselves.
User stories
As a CS engineer with a paused case, I can resume work and send a public reply without leaving
the composer to find the resume control elsewhere on the page.
Release note
The case reply composer now explains why the public-reply toggle is disabled next to the
toggle itself, and — when resuming the case's work would fix it — offers a one-click way to do
that inline.
Documentation
N/A — no published documentation covers this internal UI behavior.
Training
N/A.
Certification
N/A.
Marketing
N/A.
Automation tests
CsmCaseCommentInput(previously untested) — 5 tests coveringthe quick-fix's visibility conditions, its click behavior, its pending state, and the
no-duplicate-message fix. Mocks
@api/backend/client(pulled in transitively viaCsmUploadAttachmentModal→useCsmCaseAttachments) and stubs the rich-textEditorto aplain textarea, matching
EditCaseDetailsDialog.test.tsx's precedent for the same dependency.tsc -b,eslintclean on all three touched files.vitest run src/features/csm-cases: passes except the 2 pre-existingLinkCaseDialog.test.tsxfailures and
CaseActionBar.test.tsx's missing-config error, both confirmed unrelated viagit stashearlier in the same working session (unrelated to this change).vite buildclean.verified in a browser — no live backend/Asgardeo session available in this environment.
Security checks
Samples
N/A
Related PRs
None.
Migrations (if applicable)
N/A — no schema or data change, UI-only.
Test environment
./node_modules/.bin/*directly — thepnpmCLI itself errors with"packages field missing or empty" in this environment).
tsc -b,eslint,vitest,vite build— all clean as noted above.Learning
N/A.
Summary by CodeRabbit
New Features
Tests