Skip to content

[CSM Portal] Improve Operations tab UX: approval signal, stage labels, linked-record navigation, incident state guard - #1203

Merged
cloby99 merged 9 commits into
wso2-open-operations:mainfrom
rksk:operations-ux-fixes
Jul 23, 2026
Merged

cloby99 merged 9 commits into
wso2-open-operations:mainfrom
rksk:operations-ux-fixes

Conversation

@rksk

@rksk rksk commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

A UX review of the CSM portal's Operations tab (change requests / incidents) found several issues that make the approval-stage view, detail pages, and incident state control harder to use than necessary:

  • The change-request detail page showed state as a read-only Chip with no way to act on it, even when the signed-in user had a pending approval waiting on them.
  • Every approver in an approval stage renders with equal visual weight, so a stage with many "not required" approvers buries the one meaningful status.
  • Approval stages are keyed purely positionally, so two stages that happen to share a label would render as unexplained, visually-identical duplicates.
  • Linked-record fields on the change-request and incident detail pages (linked case, parent incident, change request, problem) render as plain text with no way to navigate to the referenced record, even though the app has a route for it.
  • The "no name on file" fallback for an approver reads as a data-integrity alarm ("Unknown approver") rather than a normal, expected case.
  • The incident state control offers all 6 states unconditionally with no transition guard, letting a user jump straight from New to Closed or reopen a Cancelled incident.

The first item required a new upstream endpoint (Ballerina/Go entity-service/CSM backend); the rest are display-layer / client-side validation only.

Goals

  • Let a CS engineer approve or reject a change request's approval stage directly from the portal, when they are the pending approver.
  • Make the one approver that actually needs attention easy to find in a stage with many "not required" entries.
  • Make a duplicate stage label read as an explained repeat instead of a broken render.
  • Let CS engineers jump directly to a linked case/incident/change request/problem from a detail page, when the target's record type is known safely.
  • Replace the alarming "Unknown approver" copy with friendlier wording, while keeping the raw id recoverable for debugging.
  • Only offer incident state transitions that make sense from the current state, and make terminal states visibly non-actionable.

Approach

  • Approve/Reject action (new endpoint): a new POST /change-requests/{id}/approvals/decision (proxied through a corresponding new ServiceNow scripted resource and Ballerina function) lets an approver decide their OWN pending approval row. ServiceNow's own existing business rule cascades the change request's own state automatically once the approval is decided — nothing here computes or sets CR state client-side, it purely submits the decision. In ChangeRequestApprovals.tsx, an approver row now renders Approve/Reject buttons only when it's the signed-in user's own row with status REQUESTED; any other row (someone else's pending approval, or one already decided) stays read-only. On success, both the approvals and change-request detail queries are invalidated so the page reflects the new state/stage immediately.
  • ChangeRequestApprovals.tsx: within a stage, non-NOT_REQUIRED approvers render first; NOT_REQUIRED approvers collapse behind a default-collapsed "N not required" disclosure. Stage labels that repeat (case-insensitively) get a (1 of 2) / (2 of 2) suffix, in encounter order; single-occurrence stages are untouched. The missing-name fallback is now "Unnamed approver", with the approver's id shown in a tooltip.
  • New shared EntityRefLink component: renders a {id, name} reference as a clickable chip to ${routeBase}/${id} when the caller can determine the target's route safely, or as plain text otherwise.
  • Wired into CsmChangeRequestDetailPage.tsx (linked case → /cases/:id) and CsmIncidentDetailPage.tsx (parent incident → /operations/incidents/:id, change request → /operations/change-requests/:id, problem → /operations/problems/:id). An incident's "caused by" field is deliberately left as plain text since its target record type can't be determined from the data alone.
  • New getLegalNextIncidentStates(current) helper defining the incident lifecycle's legal transitions (New → In Progress/Cancelled; In Progress → On Hold/Resolved/Cancelled; On Hold → In Progress/Cancelled; Resolved → Closed/In Progress (reopen); Closed/Cancelled terminal). EditIncidentDialog.tsx's state Select now only offers the current state plus its legal next states, and disables the control entirely once a terminal state is reached. This is a net-new CSM platform decision (ServiceNow itself enforces no transition order on incidents today), not a port of existing ServiceNow behavior.

User stories

As a CS engineer with a pending change-request approval, I can approve or reject it directly from the detail page instead of going into ServiceNow. As a CS engineer reviewing a change request's approvals, I can quickly see which approver still needs to act without scanning past a dozen "not required" rows. As a CS engineer on an incident or change-request detail page, I can click through to a linked case/incident/change request/problem instead of copying an id to search for it manually. As a CS engineer updating an incident's state, I only see the transitions that are actually legal from where it is now, and I can tell at a glance when an incident is closed and no longer editable.

Release note

Operations tab: change-request approvers can now approve/reject their own pending approval directly from the detail page; approval stages surface the approvers that matter and collapse the rest; linked-record references on change request/incident pages are now clickable where the target route is known; incident state changes are now restricted to valid transitions.

Documentation

N/A — internal CS-engineer portal UI/workflow improvement, no user-facing product docs affected.

Automation tests

  • Unit tests
    • ChangeRequestApprovals.test.tsx: NOT_REQUIRED collapsing/sorting, duplicate-stage suffixing, friendlier unnamed-approver fallback, Approve/Reject button visibility rules (own pending row only), click → mutate args, disabled-while-pending.
    • useDecideChangeRequestApproval.test.tsx (new): mutation hook behavior via renderHook.
    • CsmChangeRequestDetailPage.test.tsx: linked case renders as a clickable reference; dash assertion scoped to the "Linked case" field specifically.
    • CsmIncidentDetailPage.test.tsx: parent incident / change request / problem render as clickable references; "caused by" stays plain text.
    • utils/__tests__/incidents.test.ts (new): exhaustive transition-graph coverage for all 6 incident states, including both terminal states.
    • EditIncidentDialog.test.tsx (new): confirms only legal next states render as options from a non-trivial state, and that the control is disabled once terminal.
    • Full suite: pnpm test passes aside from 3 pre-existing, unrelated failing files.
  • Integration tests
    • CSM backend: change_requests_test.go covers the new handler (auth, UUID validation, body-size/JSON validation, forwarding, upstream error mapping).
    • Go entity-service: go test ./... passes for the new service method.

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 — Go/TypeScript; go vet, eslint, and tsc (via pnpm build) ran clean instead.
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes

Related PRs

This webapp change depends on a corresponding Go entity-service change (bundled in this same PR/branch) and a Ballerina entity-service change tracked separately in the private repo.

Test environment

Verified with pnpm build/pnpm test/pnpm lint (webapp), go build/go test/go vet (entity-service), and make build/make test/make vet (backend).

Summary by CodeRabbit

  • New Features
    • Added approve/reject actions for a user’s pending change-request approval, including backend decision submission and automatic UI refresh.
    • Enhanced change-request approvals display with richer approver rows, clearer stage labels (including duplicate counts), and expandable “not required” approvers.
    • Introduced reusable linked-reference rendering for linked records (with an em-dash placeholder when missing).
  • Bug Fixes
    • Improved handling for unnamed approvers and ensured approve/reject actions show only for the user’s own pending row (and are disabled while submitting).
    • Enforced “Linked case” rendering as a link only when present.
    • Restricted incident State editing to legally allowed next states, disabling terminal transitions.
  • Tests
    • Added end-to-end coverage for approval decisions, navigation links, and incident state transition behavior.

…, linked-record navigation

Reduces noise and improves scannability in the change-request approvals card
and the change-request/incident detail pages:

- Sort each approval stage's approvers so a meaningful status (approved,
  requested, rejected, etc.) surfaces first, and collapse "not required"
  approvers behind a default-collapsed disclosure instead of listing every
  one inline.
- Give repeated stage labels within one change request's approvals a
  "(N of M)" suffix so a duplicate reads as an explained repeat rather than
  an unexplained one.
- Replace the "Unknown approver" fallback with "Unnamed approver" (with the
  raw id available on hover) so a missing display name doesn't read as a
  data-integrity alarm.
- Make linked-record references clickable when the target's route is known
  safely: a change request's linked case, and an incident's parent incident /
  change request / problem. Fields whose target type can't be determined
  safely (e.g. an incident's "caused by") are left as plain text rather than
  guessing at a possibly-wrong link.
@coderabbitai

coderabbitai Bot commented Jul 22, 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: 305d0b86-4b13-46c4-bd39-24417b372666

📥 Commits

Reviewing files that changed from the base of the PR and between 866ff88 and f2a24a1.

📒 Files selected for processing (12)
  • apps/csm-portal/backend/cmd/server/main.go
  • apps/csm-portal/backend/internal/entity/entity.go
  • apps/csm-portal/backend/internal/handler/change_requests_test.go
  • apps/csm-portal/backend/internal/handler/helpers_test.go
  • apps/csm-portal/backend/openapi.yaml
  • apps/csm-portal/webapp/src/api/backend/types.ts
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.tsx
  • entity-service/internal/domain/entity.go
  • entity-service/internal/server/routes.go
  • entity-service/internal/service/interfaces.go
  • entity-service/internal/service/sn_change_request_service.go
🚧 Files skipped from review as they are similar to previous changes (11)
  • entity-service/internal/server/routes.go
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.tsx
  • apps/csm-portal/backend/cmd/server/main.go
  • apps/csm-portal/webapp/src/api/backend/types.ts
  • entity-service/internal/domain/entity.go
  • entity-service/internal/service/interfaces.go
  • apps/csm-portal/backend/internal/entity/entity.go
  • entity-service/internal/service/sn_change_request_service.go
  • apps/csm-portal/backend/internal/handler/helpers_test.go
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.test.tsx
  • apps/csm-portal/backend/internal/handler/change_requests_test.go

📝 Walkthrough

Walkthrough

The PR adds change-request approval decisions across the entity service, portal backend, and webapp; introduces navigable linked-record references; and restricts incident state editing to legal transitions. Tests cover API validation, approval actions, navigation, and terminal states.

Changes

Change request approval decisions

Layer / File(s) Summary
Approval decision contract and service flow
entity-service/internal/..., apps/csm-portal/backend/openapi.yaml
Adds the approval decision endpoint, typed payloads, validation, ServiceNow handling, routing, and JSON responses.
Portal approval API wiring
apps/csm-portal/backend/internal/..., apps/csm-portal/webapp/src/api/backend/types.ts, apps/csm-portal/webapp/src/features/csm-operations/api/*
Forwards approval decisions through the portal and adds cache-invalidating React Query mutation support.
Approval rendering and decision actions
apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals*
Adds repeated-stage labels, collapsed not-required approvers, unnamed-approver handling, and current-user Approve/Reject actions with tests.

Entity reference navigation

Layer / File(s) Summary
Entity reference link component
apps/csm-portal/webapp/src/features/csm-operations/components/EntityRefLink.tsx
Adds reusable missing, plain-text, and navigable entity-reference rendering.
Detail page linked-record wiring
apps/csm-portal/webapp/src/features/csm-operations/pages/*DetailPage*
Change request and incident pages use navigable references for supported linked records while retaining plain rendering for unsupported targets.

Incident state transition guardrails

Layer / File(s) Summary
Incident transition rules and dialog wiring
apps/csm-portal/webapp/src/features/csm-operations/utils/incidents*, apps/csm-portal/webapp/src/features/csm-operations/components/EditIncidentDialog*
Defines legal state transitions, limits selector options, disables terminal-state selectors, and tests the resulting behavior.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ApprovalsUI
  participant PortalAPI
  participant EntityService
  participant ServiceNow
  User->>ApprovalsUI: Click Approve or Reject
  ApprovalsUI->>PortalAPI: POST approval decision
  PortalAPI->>EntityService: Forward decision request
  EntityService->>ServiceNow: Submit decision
  ServiceNow-->>EntityService: Return approval state
  EntityService-->>PortalAPI: Return decision response
  PortalAPI-->>ApprovalsUI: Complete mutation and invalidate caches
Loading

Possibly related PRs

Suggested labels: Type/New Feature, Type/Improvement, Area/Backend, Entity Service

Suggested reviewers: rashmika998

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main UX improvements in the Operations tab and matches the changeset.
Description check ✅ Passed The description covers the key template sections with substantive purpose, goals, approach, tests, security, and release note details.
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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
apps/csm-portal/backend/openapi.yaml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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: 1

🧹 Nitpick comments (2)
apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.test.tsx (1)

126-145: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the tooltip-exposed ID.

This test verifies the "Unnamed approver" fallback but doesn't assert the tooltip actually exposes approver.id (a PR-stated goal). Consider adding a check for the tooltip title/content.

🤖 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-operations/components/ChangeRequestApprovals.test.tsx`
around lines 126 - 145, Extend the test for the unnamed approver in
ChangeRequestApprovals to assert that the rendered tooltip exposes the approver
ID "no-name-id" in its title or content, while preserving the existing "Unnamed
approver" and absence of "Unknown approver" assertions.
apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.tsx (1)

95-163: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Link the not required toggle to its region with aria-controls. The button already sets aria-expanded; add a stable id to the collapsible Box and reference it from the Button so the disclosure relationship is explicit for assistive tech.

🤖 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-operations/components/ChangeRequestApprovals.tsx`
around lines 95 - 163, Update the not-required disclosure in ApprovalStage by
adding a stable id to the collapsible Box containing notRequiredApprovers and
setting the toggle Button’s aria-controls to that id, while preserving the
existing aria-expanded behavior.
🤖 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-operations/pages/CsmChangeRequestDetailPage.test.tsx`:
- Around line 81-85: Update the test case “renders a dash for the linked case
when there is no case reference” to assert the dash specifically within the
“Linked case” field/container rather than using the page-wide getAllByText
lookup. Preserve the existing null-case setup and verify that the linked-case
value renders “—”.

---

Nitpick comments:
In
`@apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.test.tsx`:
- Around line 126-145: Extend the test for the unnamed approver in
ChangeRequestApprovals to assert that the rendered tooltip exposes the approver
ID "no-name-id" in its title or content, while preserving the existing "Unnamed
approver" and absence of "Unknown approver" assertions.

In
`@apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.tsx`:
- Around line 95-163: Update the not-required disclosure in ApprovalStage by
adding a stable id to the collapsible Box containing notRequiredApprovers and
setting the toggle Button’s aria-controls to that id, while preserving the
existing aria-expanded behavior.
🪄 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: aab26bff-fece-410f-9853-afae6059cbda

📥 Commits

Reviewing files that changed from the base of the PR and between 59831a5 and e66f129.

📒 Files selected for processing (7)
  • apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/components/EntityRefLink.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmIncidentDetailPage.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmIncidentDetailPage.tsx

The Incident state Select offered all 6 states unconditionally with no
transition guard, letting a user jump NEW -> CLOSED directly or reopen a
CANCELLED incident. ServiceNow enforces no state-order rule for incidents
in this org (checked business rules/ACLs: only role-gating), so this is a
net-new CSM-platform guardrail, not a port of existing SN behavior.

Adds getLegalNextIncidentStates() (standard ITSM lifecycle graph) to
utils/incidents.ts and restricts EditIncidentDialog's State select to the
incident's current state plus its legal next states. CLOSED and CANCELLED
are terminal (no outgoing transitions), so the Select is disabled for
those with a helper note.
@rksk rksk changed the title [CSM Portal] Improve Operations tab UX: approval signal, stage labels, linked-record navigation [CSM Portal] Improve Operations tab UX: approval signal, stage labels, linked-record navigation, incident state guard Jul 22, 2026
rksk added 4 commits July 22, 2026 15:56
The page renders multiple empty fields as a dash, so asserting on any
dash anywhere on the page didn't actually guard the linked-case fallback
specifically.
Adds POST /change-requests/{id}/approvals/decision, proxying to the new
Ballerina cs-entity-service resource so an authorized approver can decide
their own pending approval on a change request. ServiceNow's existing
business rule cascades the change request's own state automatically, so
this service does not compute or set state itself.
Adds POST /change-requests/{id}/approvals/decision, proxying to the new
entity-service endpoint so an authorized approver can decide their own
pending approval. Any user with change-request access may attempt a
decision; ServiceNow itself enforces that only the caller's own pending
approval can be acted on.
Adds the Approve/Reject action to the Operations-tab change request
detail page's Approvals section, closing the gap where CR state was a
read-only Chip with no transition control. The action only renders on
the current user's own pending ("REQUESTED") approval row, determined
from the existing GET /approvals response; any other row (someone
else's pending approval, or one already decided) stays read-only.
ServiceNow enforces that only the caller's own pending approval can be
acted on, and its existing business rule cascades the change request's
own state automatically, so this is purely a new mutation plus a
targeted UI affordance - nothing computes or sets CR state client-side.
On success, both the approvals and change-request detail queries are
invalidated so the page reflects the new state/stage immediately.
@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 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.

@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/csm-portal/backend/openapi.yaml (1)

1-1: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Decision decision/state values are lowercase while every sibling approval-status enum in this feature is UPPERCASE. ChangeRequestApproval.status uses [APPROVED, REJECTED, PENDING] and ChangeRequestApprover.status uses "APPROVED"/"NOT_REQUIRED"/"REQUESTED", but the new decision endpoint's contract uses "approved"/"rejected" throughout. This is a single design-choice root cause replicated across the contract definition and its Go mirrors; aligning casing now (before any consumer hardens against it) avoids a confusing, inconsistent API surface for this same feature area.

  • apps/csm-portal/backend/openapi.yaml#L1981-2049: change the decision enum and state description to use APPROVED/REJECTED (matching ChangeRequestApproval.status), or explicitly document why this endpoint intentionally diverges.
  • apps/csm-portal/backend/openapi.yaml#L6103-6122: update ChangeRequestApprovalDecisionPayload.decision enum and ChangeRequestApprovalDecisionResponse.state to UPPERCASE values consistent with ChangeRequestApprovalStatus.
  • entity-service/internal/domain/entity.go#L2009-2022: update the Decision/State doc comments and expected values to match the corrected casing (and consider typing State as domain.ChangeRequestApprovalStatus rather than a bare string, for consistency with the existing approvals model).
  • entity-service/internal/service/sn_change_request_service.go#L858-909: update changeRequestApprovalDecisions map keys and the snChangeRequestApprovalDecisionPayload/snChangeRequestApprovalDecisionResponse field values to the corrected casing, verifying against the actual Choreo contract before changing wire values.
🤖 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/backend/openapi.yaml` at line 1, Align the decision
endpoint’s status casing across the OpenAPI contract and Go mirrors: update the
decision/state enums and descriptions in the schemas, the Decision/State
documentation in the domain model, and the decision map and payload/response
values in changeRequestApprovalDecisions and related types to use
APPROVED/REJECTED consistently; type State as domain.ChangeRequestApprovalStatus
if compatible with the existing model, and verify the Choreo wire contract
before changing serialized values.
🧹 Nitpick comments (1)
apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.test.tsx (1)

200-211: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Scope the Approve/Reject assertion to the current user's row, not just a count.

This test only checks that exactly one "Approve"/"Reject" exists on screen; it doesn't verify they render specifically on the "Current User" row rather than "Other Approver"'s row. A logic inversion bug that attaches decision actions to the wrong approver would still pass this test since the count stays at 1.

♻️ Suggested refactor
       fireEvent.click(screen.getByText("Authorize"));

-      expect(screen.getByText("Current User")).toBeInTheDocument();
-      expect(screen.getByText("Other Approver")).toBeInTheDocument();
-      expect(screen.getAllByText("Approve")).toHaveLength(1);
-      expect(screen.getAllByText("Reject")).toHaveLength(1);
+      const myRow = screen.getByText("Current User").closest("li, tr, div") as HTMLElement;
+      const otherRow = screen.getByText("Other Approver").closest("li, tr, div") as HTMLElement;
+      expect(within(myRow).getByText("Approve")).toBeInTheDocument();
+      expect(within(myRow).getByText("Reject")).toBeInTheDocument();
+      expect(within(otherRow).queryByText("Approve")).not.toBeInTheDocument();
+      expect(within(otherRow).queryByText("Reject")).not.toBeInTheDocument();

Adjust the container selector to match the actual row/list-item element used by ChangeRequestApprovals.tsx.

🤖 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-operations/components/ChangeRequestApprovals.test.tsx`
around lines 200 - 211, Update the test case “shows Approve/Reject only on the
current user's own pending approval row” to select the actual approval
row/list-item container used by ChangeRequestApprovals, then assert Approve and
Reject are present within the “Current User” row and absent from the “Other
Approver” row rather than checking global counts.
🤖 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/backend/internal/handler/change_requests.go`:
- Around line 202-207: Update the approval decision handling around
DecideChangeRequestApproval to strictly decode a single JSON object, reject
empty, null, malformed, or unknown-field payloads, and accept only approved or
rejected decisions before forwarding to the entity. In
apps/csm-portal/backend/internal/handler/change_requests_test.go lines 309-318,
add 400-response cases covering empty/object-null payloads, unknown fields, and
unsupported decisions.

In
`@apps/csm-portal/webapp/src/features/csm-operations/components/EditIncidentDialog.tsx`:
- Around line 355-360: Update renderSelect to render opts.helperText as
FormHelperText inside the surrounding FormControl, preserving the existing
disabled-select behavior and displaying the terminal-state explanation when
isStateTerminal is true.

---

Outside diff comments:
In `@apps/csm-portal/backend/openapi.yaml`:
- Line 1: Align the decision endpoint’s status casing across the OpenAPI
contract and Go mirrors: update the decision/state enums and descriptions in the
schemas, the Decision/State documentation in the domain model, and the decision
map and payload/response values in changeRequestApprovalDecisions and related
types to use APPROVED/REJECTED consistently; type State as
domain.ChangeRequestApprovalStatus if compatible with the existing model, and
verify the Choreo wire contract before changing serialized values.

---

Nitpick comments:
In
`@apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.test.tsx`:
- Around line 200-211: Update the test case “shows Approve/Reject only on the
current user's own pending approval row” to select the actual approval
row/list-item container used by ChangeRequestApprovals, then assert Approve and
Reject are present within the “Current User” row and absent from the “Other
Approver” row rather than checking global counts.
🪄 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: fb483fb8-27d4-4045-a048-47ea2a3e6911

📥 Commits

Reviewing files that changed from the base of the PR and between e66f129 and c53d2a0.

📒 Files selected for processing (21)
  • apps/csm-portal/backend/cmd/server/main.go
  • apps/csm-portal/backend/internal/entity/entity.go
  • apps/csm-portal/backend/internal/handler/change_requests.go
  • apps/csm-portal/backend/internal/handler/change_requests_test.go
  • apps/csm-portal/backend/internal/handler/helpers_test.go
  • apps/csm-portal/backend/openapi.yaml
  • apps/csm-portal/webapp/src/api/backend/types.ts
  • apps/csm-portal/webapp/src/features/csm-operations/api/useDecideChangeRequestApproval.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/api/useDecideChangeRequestApproval.ts
  • apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/components/EditIncidentDialog.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/components/EditIncidentDialog.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/utils/__tests__/incidents.test.ts
  • apps/csm-portal/webapp/src/features/csm-operations/utils/incidents.ts
  • entity-service/internal/domain/entity.go
  • entity-service/internal/handler/change_request_handler.go
  • entity-service/internal/server/routes.go
  • entity-service/internal/service/interfaces.go
  • entity-service/internal/service/sn_change_request_service.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/components/ChangeRequestApprovals.tsx

Comment thread apps/csm-portal/backend/internal/handler/change_requests.go Outdated
rksk added 2 commits July 23, 2026 00:02
… select

renderSelect accepted a helperText option but never rendered it, so the
disabled terminal-state explanation was silently dropped.
…load

DecideChangeRequestApproval only checked json.Valid, so any well-formed
JSON body was forwarded to the entity service regardless of shape. Decode
into a typed payload, reject unknown fields and trailing data, and only
accept decision values of "approved" or "rejected".
@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

# Conflicts:
#	apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.test.tsx
@cloby99
cloby99 merged commit c2d194a into wso2-open-operations:main Jul 23, 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