Skip to content

feat(frontend): saga timeline + narrator on order detail (epic #130 Phase 3)#167

Merged
emeraldleaf merged 4 commits into
mainfrom
feat/saga-narrator
Jul 12, 2026
Merged

feat(frontend): saga timeline + narrator on order detail (epic #130 Phase 3)#167
emeraldleaf merged 4 commits into
mainfrom
feat/saga-narrator

Conversation

@emeraldleaf

@emeraldleaf emeraldleaf commented Jul 2, 2026

Copy link
Copy Markdown
Owner

What

Phase 3 of epic #130 — the saga narrator, the demo's centerpiece: the order-detail page now shows a live Placed → Paid → Shipped timeline that advances as the backend saga runs, with a per-step panel explaining what the backend just did:

  • Placed → teaches 202-Accepted + the transactional outbox (event staged in the same DB transaction) + RabbitMQ fanout publish
  • Paid → teaches the consume→re-publish hop + idempotent consumers under at-least-once delivery
  • Shipped → teaches the punchline: choreography, no orchestrator
  • PaymentFailed/Cancelled → the failure branch renders loud (red terminal panel), not as a stuck timeline

How

  • saga.tsOrderStatus union mirroring OrderService.Domain.OrderStatus, the step model + narration copy, deriveStepStates / isSagaSettled. Narration is a projection of the real architecture (per frontend canon: "the saga-narrator feature IS the documentation").
  • orderByIdQuery polls every 2 s via dynamic refetchInterval while the saga is in flight and stops the moment it settles — polling lives in the query definition, so there are no component-level timers.
  • SagaTimeline.tsx is pure render off the status prop; aria-current="step" marks the live step (not color-only).

Architecture-review gate

Assessed per the CLAUDE.md gate: pattern-conforming — an epic-planned frontend phase following the established feature-folder + TanStack Query patterns, no new dependencies, no cross-cutting changes, single feature folder. Proceeding under the gate's pattern-conforming carve-out (no agent pass).

Test plan

  • 11 new tests, AAA-narrated per canon: helper table tests pin the full per-status visual contract + polling lifecycle; RTL tests cover in-flight (aria-current + 202/outbox narration + polling hint), mid-saga (payment-hop + idempotency narration), settled (no active step, no polling hint, choreography narration), and failure (loud terminal panel).
  • tsc clean, eslint clean, vitest 24/24.
  • Live verification against the RabbitMQ stack (watching the timeline advance in a browser) is the natural demo — not yet run in this PR; the saga's 4-second Placed→Shipped hop was verified backend-side in feat(messaging): adopt RabbitMQ, remove Azure Service Bus (closes #148) #159.

Part of #130 (Phase 3).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a live order saga progress timeline with stage-by-stage visuals and narration.
    • Introduced a backend-driven polling experience that updates the order details until the order reaches a final state.
    • Added enhanced failure and cancellation messaging, including clear terminal stop descriptions.
  • Bug Fixes
    • Improved order status handling and typing to ensure consistent, accurate progress rendering.

…hase 3)

The centerpiece feature: the order-detail page now renders a live Placed → Paid → Shipped
timeline off the polled order status, with a narrator panel explaining what the backend
just did at each hop (202 + transactional outbox → RabbitMQ fanout → idempotent consumers
→ choreography, no orchestrator). Unblocked by the saga actually flowing locally on
RabbitMQ (PR #159).

- features/orders/saga.ts: OrderStatus union mirroring the backend enum, step model with
  teaching-grade narration copy, deriveStepStates + isSagaSettled.
- api/orders.ts: status typed as OrderStatus; orderByIdQuery polls every 2s via dynamic
  refetchInterval while the saga is in flight and stops once it settles
  (Shipped/Delivered/Cancelled/PaymentFailed). Polling lives in the query definition, not
  component timers.
- components/SagaTimeline.tsx: pure-render timeline + narrator panel; aria-current marks
  the live step; failure branch (PaymentFailed/Cancelled) renders loud, not stuck.
- Tests (11 new, AAA-narrated per canon): saga helper table tests pin the full visual
  contract per status + the polling lifecycle; RTL component tests cover in-flight,
  settled, and failure states by role/label.

typecheck + eslint clean; vitest 24/24.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a7537119-dafe-4dea-b9fb-31be2573e0f4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Changes

Order saga timeline

Layer / File(s) Summary
Saga status model and progression
frontend/src/features/orders/saga.ts, frontend/src/features/orders/saga.test.ts
Defines typed saga statuses, lifecycle narration, settlement detection, step-state derivation, and tests for progression and terminal states.
Order query polling
frontend/src/features/orders/api/orders.ts
Uses OrderStatus for order summaries and polls order details every 2000ms until the saga settles.
Live saga timeline rendering
frontend/src/features/orders/components/SagaTimeline.tsx, frontend/src/features/orders/components/OrderDetail.tsx, frontend/src/features/orders/components/SagaTimeline.test.tsx
Renders saga steps, narration, completion or failure states, and polling guidance in order details, with component coverage across saga statuses.

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

Sequence Diagram(s)

sequenceDiagram
  participant OrderDetail
  participant orderByIdQuery
  participant SagaTimeline
  OrderDetail->>orderByIdQuery: load order status
  orderByIdQuery-->>OrderDetail: return current status
  OrderDetail->>SagaTimeline: pass data.status
  SagaTimeline->>SagaTimeline: derive states and narration
  SagaTimeline-->>OrderDetail: render live timeline
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the saga timeline and narration to the order detail page for Phase 3.
Docstring Coverage ✅ Passed Docstring coverage is 100.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
  • Commit unit tests in branch feat/saga-narrator

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.

@emeraldleaf

Copy link
Copy Markdown
Owner Author

CI failure triage: not this PR's code. The failing build/integration checks are NU1903 — the Microsoft.OpenApi 2.0.0 advisory (GHSA-v5pm-xwqc-g5wc) landed in the NuGet vulnerability DB after this branch was cut, and it fails every main-based build under NuGetAudit + TreatWarningsAsErrors (confirmed in the run log). The pin (first-patched 2.7.5) ships in #166. Merge order: #166#159#167 — once #166 lands, update this branch from main and CI goes green. The frontend check (this PR's actual surface) passes.

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@frontend/src/features/orders/components/SagaTimeline.test.tsx`:
- Around line 1-62: Add a test in the SagaTimeline describe block for
status="Cancelled" that verifies the terminal heading "Saga stopped: Cancelled"
and confirms the "What the backend just did" panel renders the
Cancelled-specific narration. Keep the assertions focused on the distinct
terminal narration and visual state, matching the existing PaymentFailed
coverage.

In `@frontend/src/features/orders/saga.ts`:
- Around line 56-65: Update the order progress rendering around the PROGRESS
mapping so Cancelled does not always use the Paid-step failure position.
Preserve the distinct cancellation paths from Order.Cancel by rendering
cancellation separately or carrying the prior order state, ensuring cancellation
from Placed does not mark payment as failed while cancellation from Paid remains
accurate.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 624fc564-fff3-468a-8123-5f8ef867c84e

📥 Commits

Reviewing files that changed from the base of the PR and between f13c6fe and a11cd6b.

📒 Files selected for processing (6)
  • frontend/src/features/orders/api/orders.ts
  • frontend/src/features/orders/components/OrderDetail.tsx
  • frontend/src/features/orders/components/SagaTimeline.test.tsx
  • frontend/src/features/orders/components/SagaTimeline.tsx
  • frontend/src/features/orders/saga.test.ts
  • frontend/src/features/orders/saga.ts

Comment thread frontend/src/features/orders/components/SagaTimeline.test.tsx
Comment thread frontend/src/features/orders/saga.ts
…p the status can't blame

CodeRabbit findings on #167: Cancel() is legal from both Placed and Paid, and the DTO
carries only the current status — so PROGRESS[Cancelled]=1 rendering Paid as 'failed'
misstated a cancel-before-payment. Cancelled now renders Placed complete + remaining
steps pending; the terminal panel (Saga stopped: Cancelled) carries the story. Adds the
missing Cancelled error-path tests (derivation table + timeline render), per the
frontend canon's error-path coverage rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@emeraldleaf

Copy link
Copy Markdown
Owner Author

Both findings addressed in 4eb6c62: Cancelled no longer reuses the failure marker — the status alone can't say whether payment happened (Cancel is legal from Placed AND Paid), so the remaining steps render pending and the terminal panel tells the story; added the missing Cancelled error-path tests (derivation table + timeline render, incl. asserting the ✕ glyph stays PaymentFailed-only). typecheck/lint clean, vitest 26/26.

@emeraldleaf

Copy link
Copy Markdown
Owner Author

@coderabbitai resolve

@emeraldleaf

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
✅ 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 commented Jul 12, 2026

Copy link
Copy Markdown
✅ Action performed

Comments resolved and changes approved.

@emeraldleaf
emeraldleaf merged commit b09af0a into main Jul 12, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant