Skip to content

[CSM Portal] Inline resume-work quick-fix for the locked public-reply toggle - #1369

Merged
Rashmika998 merged 6 commits into
wso2-open-operations:mainfrom
Hesara2003:feature/csm-comment-resume-work-quickfix
Aug 5, 2026
Merged

[CSM Portal] Inline resume-work quick-fix for the locked public-reply toggle#1369
Rashmika998 merged 6 commits into
wso2-open-operations:mainfrom
Hesara2003:feature/csm-comment-resume-work-quickfix

Conversation

@Hesara2003

@Hesara2003 Hesara2003 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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

CsmCaseCommentInput gets three new optional props: canResumeToUnlockPublicReply,
onResumeWork, isResumingWork. When the reply is locked and resuming would unlock it, a line
appears 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.

CsmCaseDetailPage computes canResumeToUnlockPublicReply (case is work_in_progress and
already assigned, just not ongoing) and wires onResumeWork straight to the existing
onAction({ secondary: "toggle_work_state" }) handler — the same one the case header's own
Resume 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

  • Unit tests: first test file for CsmCaseCommentInput (previously untested) — 5 tests covering
    the quick-fix's visibility conditions, its click behavior, its pending state, and the
    no-duplicate-message fix. Mocks @api/backend/client (pulled in transitively via
    CsmUploadAttachmentModaluseCsmCaseAttachments) and stubs the rich-text Editor to a
    plain textarea, matching EditCaseDetailsDialog.test.tsx's precedent for the same dependency.
  • tsc -b, eslint clean on all three touched files.
  • vitest run src/features/csm-cases: passes except the 2 pre-existing LinkCaseDialog.test.tsx
    failures and CaseActionBar.test.tsx's missing-config error, both confirmed unrelated via
    git stash earlier in the same working session (unrelated to this change).
  • Production vite build clean.
  • Integration tests: none — no integration-test harness exists for this app. Not manually
    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.js, pnpm (via ./node_modules/.bin/* directly — the pnpm CLI itself errors with
    "packages field missing or empty" in this environment).
  • macOS.
  • tsc -b, eslint, vitest, vite build — all clean as noted above.
  • Not manually verified in a browser against a live backend in this session.

Learning

N/A.

Summary by CodeRabbit

  • New Features

    • Added a Resume work action to the public reply composer when work is paused.
    • Resuming work unlocks public replies and shows a disabled pending state while processing.
    • Improved work-state status display for paused cases, including cases with missing status information.
    • Prevented duplicate lock reasons from appearing when the quick action is available.
  • Tests

    • Added coverage for resume-work availability, button states, callback handling, and lock-reason scenarios.

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.
@coderabbitai

coderabbitai Bot commented Aug 5, 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 Plus

Run ID: 6f3854e6-f7b4-4574-af68-91acc9a797c3

📥 Commits

Reviewing files that changed from the base of the PR and between 68d9d9a and fa46673.

📒 Files selected for processing (5)
  • apps/csm-portal/webapp/src/features/csm-cases/components/CasePreviewDrawer.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CasesList.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/utils/caseWorkState.test.ts
  • apps/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

📝 Walkthrough

Walkthrough

The 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 paused. Tests cover rendering, callbacks, eligibility, normalization, and button disabling.

Changes

Public reply resume flow

Layer / File(s) Summary
Resume eligibility contract
apps/csm-portal/webapp/src/features/csm-cases/utils/caseWorkState.ts, apps/csm-portal/webapp/src/features/csm-cases/utils/caseWorkState.test.ts
canResumeToUnlockPublicReply allows resume-work for the current assignee on a paused work_in_progress case. effectiveWorkState maps missing values to paused. Tests cover eligibility and normalization.
Composer resume behavior
apps/csm-portal/webapp/src/features/csm-cases/components/CsmCaseCommentInput.tsx, apps/csm-portal/webapp/src/features/csm-cases/components/CsmCaseCommentInput.test.tsx
The composer accepts resume-work props, shows an inline action when eligible, hides the duplicate lock reason, and disables the action while resuming.
Case detail and list integration
apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx, apps/csm-portal/webapp/src/features/csm-cases/components/CasePreviewDrawer.tsx, apps/csm-portal/webapp/src/features/csm-cases/components/CasesList.tsx
The detail page computes resume eligibility and passes the callback and pending state to the composer. Work-state chips use effectiveWorkState for all work_in_progress cases.

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
Loading

Possibly related PRs

Suggested labels: Type/New Feature

Suggested reviewers: rksk, cloby99, rashmika998

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the required sections, explains the implementation and testing, and clearly documents known limitations.
Title check ✅ Passed The title clearly and concisely describes the inline resume-work quick-fix for locked public replies.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
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.

@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

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between e4f0c10 and b039f7f.

📒 Files selected for processing (3)
  • apps/csm-portal/webapp/src/features/csm-cases/components/CsmCaseCommentInput.test.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CsmCaseCommentInput.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx

Comment thread apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx Outdated
…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.
@madubashinii

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between b039f7f and 68d9d9a.

📒 Files selected for processing (3)
  • apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/utils/caseWorkState.test.ts
  • apps/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.
@s-sandali

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Rashmika998
Rashmika998 merged commit 6f4fd7e into wso2-open-operations:main Aug 5, 2026
1 check passed
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.

4 participants