Skip to content

[CSM Portal] show state transitions and field updates in case activity stream - #1064

Merged
Rashmika998 merged 4 commits into
wso2-open-operations:mainfrom
rksk:csm-case-activities-fe
Jul 6, 2026
Merged

Rashmika998 merged 4 commits into
wso2-open-operations:mainfrom
rksk:csm-case-activities-fe

Conversation

@rksk

@rksk rksk commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Purpose

The case activity feed's lifecycle lane ("State changes") was always empty on the client — the audit array was hardcoded to []. Meanwhile the backend now exposes a dedicated activities endpoint (POST /cases/{id}/activities/search) that returns audited field/state changes for a case. This PR wires that endpoint's field_change entries into the existing lifecycle lane so engineers can see when a case's state, severity, or assignee changed, by whom, and what the old/new values were.

Goals

  • Populate the activity feed's "State changes" lane with real audited field/state changes instead of an always-empty list.
  • Render each changed field as <field>: <new value> was <old value>, with the old value struck through, following the agent-workspace convention for field-change history.
  • Handle "set" (no previous value) and "cleared" (no new value) cases distinctly.
  • Refresh the lane immediately after a state/severity/assignee/watcher update so a new lifecycle entry shows without a manual reload.

Approach

  • Added useGetCsmCaseActivities (new hook, mirrors the existing useGetCsmCaseComments pattern): calls POST /cases/{id}/activities/search with a single wide page (includeFieldChanges: true), then filters the response down to type === "field_change" entries and maps them onto the existing CaseAuditEntry shape (extended with an optional changes array).
  • CaseActivitiesFeed now renders a field-change entry's changes array as one line per changed field (old value struck through), falling back to the legacy description string for any audit entries that don't carry changes.
  • CsmCaseDetailPage now sources the "Activity timeline" audit lane from the new hook instead of the case detail response's always-empty audit field.
  • The case-patch mutations (usePatchCsmCase, usePatchCsmCaseById) now also invalidate the activities query on success, since a state/severity/assignee/watcher patch is audited server-side.
  • Comments (including work notes), the linked chat transcript, and attachments are intentionally left untouched — they keep reading from their current hooks (useGetCsmCaseComments, useGetCsmConversationMessages, useGetCsmCaseAttachments). The new activities endpoint also returns comment/attachment entries, but this PR ignores them to avoid a second, divergent read path, and because the endpoint excludes work notes entirely.

Known limitations (tracked as follow-ups, not fixed here):

  • The activities endpoint excludes work notes by design, so this PR cannot and does not use it as a comments source.
  • Field changes are loaded as a single wide page (capped at the backend's page-size limit); a case with very high comment/attachment volume sharing the same underlying activity log could theoretically have field changes truncated behind that cap. If that turns out to matter in practice, a follow-up should add explicit pagination to this lane.

User stories

As a CS engineer viewing a case, I want to see when the case's state, severity, or assignee changed (and by whom) in the activity timeline, so I don't have to reconstruct that history from memory or separate audit tooling.

Release note

The case activity timeline now shows state, severity, and assignee changes (with old/new values) alongside comments and attachments, instead of leaving that lane empty.

Documentation

N/A — internal CS-engineer portal UI behavior change with no external-facing documentation.

Automation tests

  • Unit tests: added useCsmCaseActivities.test.ts covering the field-change mapper (author display-name fallback order, default-to-empty changes), and CaseActivitiesFeed.test.tsx covering field-change rendering (single change, multiple changes in one entry, cleared value, and fallback to description when changes is absent).
  • Integration tests: none added; this is a read-path UI change consuming an existing endpoint contract, exercised via the unit tests above plus manual verification against pnpm build.

Security checks

  • Followed secure coding standards in http://wso2.com/technical-reports/wso2-secure-engineering-guidelines? yes
  • Ran FindSecurityBugs plugin and verified report? N/A — TypeScript/React project; ran pnpm lint (eslint) and tsc via pnpm build clean instead.
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes

Related PRs

Stacked on top of the CSM case comments/attachments work in #1062 and #1063. Depends on a corresponding backend/entity-service change exposing POST /cases/{id}/activities/search, tracked separately.

Migrations (if applicable)

N/A — no data migrations; purely additive read-path wiring on the frontend.

Test environment

Verified locally with pnpm build, pnpm test, and pnpm lint against Node/pnpm on macOS.

Learning

N/A

Summary by CodeRabbit

  • New Features

    • Added a richer case activity timeline with detailed field-level changes, including previous and new values.
    • Case activity updates now refresh automatically after case edits, so the timeline stays current.
  • Bug Fixes

    • Improved activity display for timestamp-related changes and hidden redundant entries.
    • Updated activity counts to include all relevant timeline items.
  • Tests

    • Added coverage for activity mapping and timeline rendering behavior.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@rksk, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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

How do review 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 refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7407b385-cf92-4d09-b80d-f9c4c46603d3

📥 Commits

Reviewing files that changed from the base of the PR and between 695667e and cc48b13.

📒 Files selected for processing (10)
  • apps/csm-portal/webapp/src/api/backend/types.ts
  • apps/csm-portal/webapp/src/constants/apiConstants.ts
  • apps/csm-portal/webapp/src/features/csm-cases/api/useCsmCaseActivities.test.ts
  • apps/csm-portal/webapp/src/features/csm-cases/api/useCsmCaseActivities.ts
  • apps/csm-portal/webapp/src/features/csm-cases/api/usePatchCsmCase.ts
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActivitiesFeed.test.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActivitiesFeed.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseDetailWidgets.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/types/csmCases.ts
📝 Walkthrough

Walkthrough

This PR adds a new backend "case activities" search API contract with field-change tracking, a React Query hook (useGetCsmCaseActivities) to fetch and map field-change entries into CaseAuditEntry shape, cache invalidation on case patch, and updated CaseActivitiesFeed rendering with timestamp-aware field-change display, wired into CsmCaseDetailPage.

Changes

Case Activities Feature

Layer / File(s) Summary
Backend types and query key
apps/csm-portal/webapp/src/api/backend/types.ts, apps/csm-portal/webapp/src/constants/apiConstants.ts
Adds BeFieldChange, BeCaseActivityType, BeCaseActivityEntry, activities search request/response types, and CSM_CASE_ACTIVITIES query key.
Activities hook and mapper
apps/csm-portal/webapp/src/features/csm-cases/api/useCsmCaseActivities.ts, .../useCsmCaseActivities.test.ts
Adds auditEntryFromBeActivity mapper and useGetCsmCaseActivities hook querying and filtering field_change entries, with unit tests.
Cache invalidation
apps/csm-portal/webapp/src/features/csm-cases/api/usePatchCsmCase.ts
Invalidates CSM_CASE_ACTIVITIES query on successful case patch in both patch hooks.
Audit entry type updates
apps/csm-portal/webapp/src/features/csm-cases/types/csmCases.ts
Adds CaseAuditFieldChange interface; makes description optional and adds changes array on CaseAuditEntry.
Feed rendering and icons
apps/csm-portal/webapp/src/features/csm-cases/components/CaseActivitiesFeed.tsx, .test.tsx, CaseDetailWidgets.tsx
Adds timestamp-aware redundancy filtering, FieldChangeLine component, reworked audit card layout, field_change icon mapping, and comprehensive tests.
Page wiring
apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
Calls useGetCsmCaseActivities, updates timeline count, and passes activityAudit to CaseActivitiesFeed.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CsmCaseDetailPage
  participant useGetCsmCaseActivities
  participant BackendAPI
  participant auditEntryFromBeActivity
  participant CaseActivitiesFeed

  CsmCaseDetailPage->>useGetCsmCaseActivities: call with caseId
  useGetCsmCaseActivities->>BackendAPI: POST /cases/{id}/activities/search (includeFieldChanges: true)
  BackendAPI-->>useGetCsmCaseActivities: activity entries
  useGetCsmCaseActivities->>useGetCsmCaseActivities: filter type === "field_change"
  useGetCsmCaseActivities->>auditEntryFromBeActivity: map each entry
  auditEntryFromBeActivity-->>useGetCsmCaseActivities: CaseAuditEntry[]
  useGetCsmCaseActivities-->>CsmCaseDetailPage: activityAudit
  CsmCaseDetailPage->>CaseActivitiesFeed: audit = activityAudit ?? []
  CaseActivitiesFeed->>CaseActivitiesFeed: filter redundant timestamp changes
  CaseActivitiesFeed-->>CsmCaseDetailPage: rendered field-change lines
Loading

Possibly related PRs

Suggested labels: Type/Improvement, Type/New Feature

Suggested reviewers: cloby99, Rashmika998

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly summarizes the main change: surfacing case state transitions and field updates in the activity stream.
Description check ✅ Passed The description covers purpose, goals, approach, tests, release note, and operational details, with only a few optional template sections omitted.
✨ 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

🧹 Nitpick comments (2)
apps/csm-portal/webapp/src/api/backend/types.ts (1)

577-595: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a discriminated union instead of an optional-properties bag.

changes is documented as "only present on type === 'field_change'" but is typed as an optional field on the shared interface, so nothing stops constructing e.g. { type: "comment", changes: [...] } at compile time. A discriminated union on type would make invalid states unrepresentable and let TS narrow automatically at call sites (e.g. in auditEntryFromBeActivity).

♻️ Example discriminated-union shape
-export interface BeCaseActivityEntry {
-  id: string;
-  type: BeCaseActivityType;
-  content?: string;
-  createdOn: string;
-  createdBy?: string;
-  createdByFirstName?: string;
-  createdByLastName?: string;
-  createdByFullName?: string;
-  /** Only present on `type === "field_change"` entries. */
-  changes?: BeFieldChange[];
-}
+interface BeCaseActivityEntryBase {
+  id: string;
+  content?: string;
+  createdOn: string;
+  createdBy?: string;
+  createdByFirstName?: string;
+  createdByLastName?: string;
+  createdByFullName?: string;
+}
+
+export type BeCaseActivityEntry =
+  | (BeCaseActivityEntryBase & { type: "comment" | "attachment" })
+  | (BeCaseActivityEntryBase & { type: "field_change"; changes: BeFieldChange[] });
🤖 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/backend/types.ts` around lines 577 - 595,
`BeCaseActivityEntry` is modeled as a single optional-properties interface even
though `changes` only applies when `type` is `"field_change"`. Refactor this
type in `types.ts` into a discriminated union keyed by `type`, so
non-field-change entries cannot include `changes` and TypeScript can narrow
automatically. Update any consumers such as `auditEntryFromBeActivity` to use
the narrowed union members instead of relying on optional checks.
apps/csm-portal/webapp/src/features/csm-cases/api/useCsmCaseActivities.ts (1)

42-58: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Mapper doesn't validate entry.type before mapping.

auditEntryFromBeActivity is exported and unconditionally sets kind: "field_change" regardless of entry.type. It's currently only called after the caller filters for type === "field_change" (line 90), but nothing enforces that contract at the type level. Tying this to a discriminated BeCaseActivityEntry (see comment on types.ts) would make misuse a compile error instead of a runtime footgun for future callers.

🤖 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/useCsmCaseActivities.ts`
around lines 42 - 58, auditEntryFromBeActivity is assuming a field_change entry
without enforcing it, since it always returns kind: "field_change" regardless of
entry.type. Update the function signature to accept only the discriminated
BeCaseActivityEntry variant for field_change (or narrow inside the function
before mapping), and align the BeCaseActivityEntry type definitions so misuse
becomes a compile-time error. Keep the mapping logic in auditEntryFromBeActivity
and the caller filter in useCsmCaseActivities consistent with the discriminated
union contract.
🤖 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/CaseActivitiesFeed.tsx`:
- Around line 371-394: The CaseActivitiesFeed rendering currently leaves the
body blank when e.entry.changes exists but visibleChanges is empty after
filtering redundant timestamp updates. Update the conditional around the
changes/description rendering in CaseActivitiesFeed so it falls back to
e.entry.description whenever visibleChanges has no items, not only when
e.entry.changes is missing or empty; use the visibleChanges mapping block and
the FieldChangeLine branch as the place to make the check.

In `@apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx`:
- Around line 270-273: The activities query in CsmCaseDetailPage is missing
load/error handling, so a failed `useGetCsmCaseActivities(caseId)` request is
treated the same as “no state changes.” Update the `activityAudit` section to
destructure and use `isLoading` and `isError` from `useGetCsmCaseActivities`,
gate the skeleton with `isActivityLoading` alongside the existing loading
checks, and add an inline error notice when `isActivityError` is true, matching
the existing comments/chat handling in this file.

---

Nitpick comments:
In `@apps/csm-portal/webapp/src/api/backend/types.ts`:
- Around line 577-595: `BeCaseActivityEntry` is modeled as a single
optional-properties interface even though `changes` only applies when `type` is
`"field_change"`. Refactor this type in `types.ts` into a discriminated union
keyed by `type`, so non-field-change entries cannot include `changes` and
TypeScript can narrow automatically. Update any consumers such as
`auditEntryFromBeActivity` to use the narrowed union members instead of relying
on optional checks.

In `@apps/csm-portal/webapp/src/features/csm-cases/api/useCsmCaseActivities.ts`:
- Around line 42-58: auditEntryFromBeActivity is assuming a field_change entry
without enforcing it, since it always returns kind: "field_change" regardless of
entry.type. Update the function signature to accept only the discriminated
BeCaseActivityEntry variant for field_change (or narrow inside the function
before mapping), and align the BeCaseActivityEntry type definitions so misuse
becomes a compile-time error. Keep the mapping logic in auditEntryFromBeActivity
and the caller filter in useCsmCaseActivities consistent with the discriminated
union contract.
🪄 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: 5128b0ca-e2d0-4956-9aab-7b6e642024e6

📥 Commits

Reviewing files that changed from the base of the PR and between 7f61449 and 695667e.

📒 Files selected for processing (10)
  • apps/csm-portal/webapp/src/api/backend/types.ts
  • apps/csm-portal/webapp/src/constants/apiConstants.ts
  • apps/csm-portal/webapp/src/features/csm-cases/api/useCsmCaseActivities.test.ts
  • apps/csm-portal/webapp/src/features/csm-cases/api/useCsmCaseActivities.ts
  • apps/csm-portal/webapp/src/features/csm-cases/api/usePatchCsmCase.ts
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActivitiesFeed.test.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseActivitiesFeed.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/components/CaseDetailWidgets.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx
  • apps/csm-portal/webapp/src/features/csm-cases/types/csmCases.ts

Comment thread apps/csm-portal/webapp/src/features/csm-cases/pages/CsmCaseDetailPage.tsx Outdated
@rksk
rksk changed the base branch from dev-app-csm-portal to main July 6, 2026 13:34
rksk added 4 commits July 6, 2026 19:16
…y stream

Wire the case activity feed's lifecycle lane to the new field-change
activity endpoint. Previously the lane was always empty because the
audit list was hardcoded to an empty array on the client, so state,
severity, and assignee changes never appeared in the timeline even
though the backend now records them.

Comments, work notes, chat, and attachments keep reading from their
existing endpoints/hooks — this only adds the field-change lane
alongside them.
…ed changes (field curation moved to backend)
@rksk
rksk force-pushed the csm-case-activities-fe branch from 26fdc75 to cc48b13 Compare July 6, 2026 13:48
@Rashmika998
Rashmika998 merged commit 3641dea into wso2-open-operations:main Jul 6, 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.

2 participants