Skip to content

feat: add escrow detail page#207

Merged
sotoJ24 merged 1 commit into
safetrustcr:mainfrom
Jonatan-Chaverri:feat/add-escrow-details-page
Jun 23, 2026
Merged

feat: add escrow detail page#207
sotoJ24 merged 1 commit into
safetrustcr:mainfrom
Jonatan-Chaverri:feat/add-escrow-details-page

Conversation

@Jonatan-Chaverri

@Jonatan-Chaverri Jonatan-Chaverri commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Closes #188

Summary by CodeRabbit

  • New Features
    • Added escrow detail page displaying status-based views (pending, invoice, status, released)
    • Added notes feature to save and retrieve escrow-related notes
    • Added invoice view with acknowledgment capability
    • Integrated payment flow for pending escrows
    • Added justification and claims management for released escrows
    • Enhanced owner and tenant information displays across all views

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 565bc0cb-c0e5-4d2b-9706-95b54cfa54ea

📥 Commits

Reviewing files that changed from the base of the PR and between cd65111 and 2bec3b6.

📒 Files selected for processing (9)
  • apps/frontend/src/app/dashboard/escrow/[id]/page.tsx
  • apps/frontend/src/components/escrow/NotesPanel.tsx
  • apps/frontend/src/components/escrow/views/EscrowInvoiceView.tsx
  • apps/frontend/src/components/escrow/views/EscrowPendingView.tsx
  • apps/frontend/src/components/escrow/views/EscrowReleasedView.tsx
  • apps/frontend/src/components/escrow/views/EscrowStatusView.tsx
  • apps/frontend/src/components/escrow/views/escrow-view-utils.tsx
  • apps/frontend/src/graphql/queries/escrow-queries.ts
  • apps/frontend/src/types/escrow.ts
 ___________________________________________________________________________________________________________________________________________________________
< Don't be a slave to formal methods. Don't blindly adopt any technique without putting it into the context of your development practices and capabilities. >
 -----------------------------------------------------------------------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
📝 Walkthrough

Walkthrough

Adds a dynamic escrow detail page at /dashboard/escrow/[id] that fetches escrow data via Apollo and renders one of four status-specific sub-views. Introduces shared TypeScript types for the GraphQL response, a utility module with formatting helpers and shared styles, four sub-view components, and a localStorage-backed NotesPanel sidebar component.

Changes

Escrow Detail Page with Status-Driven Views

Layer / File(s) Summary
Escrow types and shared view utilities
apps/frontend/src/types/escrow.ts, apps/frontend/src/components/escrow/views/escrow-view-utils.tsx
Defines EscrowApartmentAddress, EscrowApartmentOwner, EscrowApartment, EscrowDetail, and GetEscrowByIdResult types. Exports shared viewStyles, InfoPair, and formatting/computation helpers (formatMoney, formatDate, formatWallet, formatOwnerName, formatOwnerPhone, depositAmount, invoiceNumber) consumed by all sub-views.
Four escrow sub-view components
apps/frontend/src/components/escrow/views/EscrowPendingView.tsx, apps/frontend/src/components/escrow/views/EscrowInvoiceView.tsx, apps/frontend/src/components/escrow/views/EscrowStatusView.tsx, apps/frontend/src/components/escrow/views/EscrowReleasedView.tsx
Implements EscrowPendingView (apartment card + EscrowPayFlow + owner contact), EscrowInvoiceView (billing table + conditional acknowledge button), EscrowStatusView (description textarea + tenant/owner panels + PdfExportButton), and EscrowReleasedView (justification + beneficiary info + claims textarea).
NotesPanel sidebar component
apps/frontend/src/components/escrow/NotesPanel.tsx
Adds NotesPanel with per-escrow localStorage persistence under escrow-notes:${escrowId}, textarea editing state, and a "Saved" indicator that clears on content change.
EscrowDetailPage orchestration
apps/frontend/src/app/dashboard/escrow/[id]/page.tsx
Validates the id route param against a UUID regex, runs useQuery with conditional skip, defines ViewKind/ViewMeta types, implements resolveView to map escrow status (including funded/active + invoiceAcknowledged transition) to view metadata, and renders InvoiceHeader, the selected sub-view, NotesPanel, and ProcessStepper.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant EscrowDetailPage
  participant ApolloClient
  participant resolveView
  participant SubView

  Browser->>EscrowDetailPage: GET /dashboard/escrow/id
  EscrowDetailPage->>EscrowDetailPage: validate UUID, skip if invalid
  EscrowDetailPage->>ApolloClient: useQuery GET_ESCROW_BY_ID
  ApolloClient-->>EscrowDetailPage: loading / error / EscrowDetail
  EscrowDetailPage->>resolveView: escrow.status + invoiceAcknowledged
  resolveView-->>EscrowDetailPage: ViewMeta with kind, step, headerStatus
  EscrowDetailPage->>SubView: renderView to pending/invoice/status/released
  SubView-->>EscrowDetailPage: rendered JSX
  EscrowDetailPage-->>Browser: InvoiceHeader + SubView + NotesPanel + ProcessStepper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • safetrustcr/dApp-SafeTrust#194: This PR's EscrowPendingView embeds ApartmentPropertyCard, which is the component introduced by that PR for the escrow pending flow.
  • safetrustcr/dApp-SafeTrust#196: The EscrowDetailPage calls GET_ESCROW_BY_ID, which is introduced in that PR alongside the Hasura apartment/escrow relationship setup.
  • safetrustcr/dApp-SafeTrust#201: This PR's EscrowStatusView and EscrowReleasedView both render PdfExportButton, which is introduced in that PR together with the GET_ESCROW_BY_ID query.

Suggested reviewers

  • sotoJ24

Poem

🐇 Hoppity hop through the escrow trail,
Four views now render without fail!
Pending, invoice, status, released —
UUID checked, loading spinner ceased.
Notes saved in localStorage with care,
A rabbit-built page beyond compare! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.75% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add escrow detail page' directly and clearly describes the main change - adding a new escrow detail page component.
Linked Issues check ✅ Passed The PR implements all primary objectives from issue #188: creates the dynamic route, fetches escrow data via GET_ESCROW_BY_ID, renders conditional views based on status (pending_signature, funded/active, completed), includes shared layout components, and implements necessary supporting components and type definitions.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the escrow detail page feature: the main page component, view components for each status, utility helpers, type definitions, and the NotesPanel sidebar component are all required for the feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

Tip

CodeRabbit can use SkillSpector to scan AI agent skill manifests and MCP configurations for security risks.

SkillSpector analyzes SKILL.md files and MCP configuration files such as mcp.json for vulnerabilities, malicious patterns, and other security issues. See the SkillSpector documentation for details.

@Jonatan-Chaverri

Copy link
Copy Markdown
Contributor Author
image-1781834160973

@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: 3

🧹 Nitpick comments (3)
apps/frontend/src/app/dashboard/escrow/[id]/page.tsx (3)

148-149: 💤 Low value

Mixed styling approach: Tailwind classes and inline styles.

The page uses Tailwind utility classes (e.g., className="flex items-center justify-center py-16" at line 148, className="grid grid-cols-1 lg:grid-cols-3" at line 185) alongside inline style objects throughout the rest of the component.

For consistency and maintainability, consider using a single styling approach across the entire page. If Tailwind is available, the inline styles could be replaced with utility classes; alternatively, inline styles could replace the Tailwind classes.

Also applies to: 185-186

🤖 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/frontend/src/app/dashboard/escrow/`[id]/page.tsx around lines 148 - 149,
The component mixes two styling approaches: Tailwind utility classes (such as in
the loading spinner div at line 148 with className="flex items-center
justify-center py-16" and the grid layout at line 185) and inline style objects
throughout the rest of the component. To improve maintainability and
consistency, standardize the styling approach across the entire page by
converting all inline style objects to their equivalent Tailwind utility
classes, since Tailwind is already being used in the component. Audit the entire
page.tsx file, identify all instances of inline styles, and replace them with
appropriate Tailwind classes that achieve the same visual result.

72-113: ⚡ Quick win

Default fallback in resolveView may mask unexpected status values.

The default case (lines 106-112) silently maps any unrecognized status to the "invoice" view. If the database or API introduces a new status value (e.g., "cancelled", "disputed"), users will see the invoice view without any indication that something is unexpected.

Consider logging unrecognized statuses to aid debugging:

    default:
+     console.warn(`Unexpected escrow status: "${status}". Falling back to invoice view.`);
      return {
        kind: "invoice",

Or, for stricter handling, return a dedicated error/unknown view that surfaces the unexpected status to the user.

🤖 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/frontend/src/app/dashboard/escrow/`[id]/page.tsx around lines 72 - 113,
The default case in the resolveView function silently maps any unrecognized
status value to the invoice view without logging or surfacing the unexpected
value. To fix this, add console logging or error tracking in the default case to
capture and report when an unexpected status is encountered (this helps with
debugging when new statuses are introduced), and optionally return a dedicated
error or unknown view configuration instead of defaulting to the invoice view,
so users get visual feedback that something unexpected occurred rather than
silently showing the invoice view.

179-181: 💤 Low value

Date formatting without explicit timezone may display unexpected dates.

The toLocaleDateString() call formats the date in the user's local timezone. If escrow.updated_at or escrow.created_at represents a UTC timestamp, users in different timezones may see different calendar dates (e.g., 11:00 PM on Jan 1 UTC displays as Jan 1 for UTC users but Dec 31 for users in earlier timezones).

If you want to display the date in a specific timezone (e.g., UTC) regardless of user location, pass explicit options:

new Date(escrow.updated_at ?? escrow.created_at).toLocaleDateString('en-US', { 
  timeZone: 'UTC',
  day: 'numeric',
  month: 'long',
  year: 'numeric'
})

Or, if you intend to show the local date, this is working as designed.

🤖 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/frontend/src/app/dashboard/escrow/`[id]/page.tsx around lines 179 - 181,
The paidAt prop assignment uses toLocaleDateString() without explicit timezone
options, which causes the date to be formatted in each user's local timezone. If
the underlying timestamp values (escrow.updated_at or escrow.created_at) are in
UTC, different users will see different calendar dates depending on their
timezone. To fix this, add explicit timezone options to the toLocaleDateString()
method call, specifying 'UTC' as the timeZone option along with day, month, and
year formatting options if you want all users to see the same date regardless of
location, or remove these options if the current behavior of showing the user's
local date is intentional.
🤖 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/frontend/src/components/escrow/NotesPanel.tsx`:
- Around line 60-70: The useEffect hook and handleSave function in
NotesPanel.tsx access localStorage without error handling, which can throw
exceptions in private browsing modes, when storage quota is exceeded, or when
disabled by security policies. Wrap both the window.localStorage.getItem call in
the useEffect and the window.localStorage.setItem call in the handleSave
function with try-catch blocks to gracefully handle these exceptions, ensuring
the component continues to render even if localStorage operations fail.

In `@apps/frontend/src/components/escrow/views/EscrowPendingView.tsx`:
- Line 21: The ownerAddress assignment in EscrowPendingView uses owner?.id as a
fallback, but this is a database UUID not a Stellar wallet address. Update the
GraphQL query to fetch the owner's primary wallet address from the user_wallets
table, then replace the owner?.id fallback with the actual wallet_address field.
Apply the same fix to EscrowStatusView (line 55) and EscrowReleasedView (line
38) where the same pattern exists, ensuring all three files properly retrieve
and use the Stellar wallet address instead of the database UUID.

In `@apps/frontend/src/components/escrow/views/EscrowStatusView.tsx`:
- Around line 34-38: The textarea element with placeholder "Description…" in
EscrowStatusView.tsx is uncontrolled and only has a defaultValue prop without a
corresponding value prop or onChange handler, meaning user changes will not be
persisted. Either convert this textarea to a controlled component by adding a
value prop (bound to state) and an onChange handler to track changes (following
the pattern used in EscrowReleasedView.tsx for claims textarea or NotesPanel.tsx
for notes), or if this field should be read-only in this view, replace the
textarea with a paragraph/div element displaying the description or add the
readOnly attribute to make non-editability explicit.

---

Nitpick comments:
In `@apps/frontend/src/app/dashboard/escrow/`[id]/page.tsx:
- Around line 148-149: The component mixes two styling approaches: Tailwind
utility classes (such as in the loading spinner div at line 148 with
className="flex items-center justify-center py-16" and the grid layout at line
185) and inline style objects throughout the rest of the component. To improve
maintainability and consistency, standardize the styling approach across the
entire page by converting all inline style objects to their equivalent Tailwind
utility classes, since Tailwind is already being used in the component. Audit
the entire page.tsx file, identify all instances of inline styles, and replace
them with appropriate Tailwind classes that achieve the same visual result.
- Around line 72-113: The default case in the resolveView function silently maps
any unrecognized status value to the invoice view without logging or surfacing
the unexpected value. To fix this, add console logging or error tracking in the
default case to capture and report when an unexpected status is encountered
(this helps with debugging when new statuses are introduced), and optionally
return a dedicated error or unknown view configuration instead of defaulting to
the invoice view, so users get visual feedback that something unexpected
occurred rather than silently showing the invoice view.
- Around line 179-181: The paidAt prop assignment uses toLocaleDateString()
without explicit timezone options, which causes the date to be formatted in each
user's local timezone. If the underlying timestamp values (escrow.updated_at or
escrow.created_at) are in UTC, different users will see different calendar dates
depending on their timezone. To fix this, add explicit timezone options to the
toLocaleDateString() method call, specifying 'UTC' as the timeZone option along
with day, month, and year formatting options if you want all users to see the
same date regardless of location, or remove these options if the current
behavior of showing the user's local date is intentional.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 475603b3-98b5-4665-8f98-737cbe3ddca4

📥 Commits

Reviewing files that changed from the base of the PR and between 63e1e68 and cd65111.

📒 Files selected for processing (8)
  • apps/frontend/src/app/dashboard/escrow/[id]/page.tsx
  • apps/frontend/src/components/escrow/NotesPanel.tsx
  • apps/frontend/src/components/escrow/views/EscrowInvoiceView.tsx
  • apps/frontend/src/components/escrow/views/EscrowPendingView.tsx
  • apps/frontend/src/components/escrow/views/EscrowReleasedView.tsx
  • apps/frontend/src/components/escrow/views/EscrowStatusView.tsx
  • apps/frontend/src/components/escrow/views/escrow-view-utils.tsx
  • apps/frontend/src/types/escrow.ts

Comment thread apps/frontend/src/components/escrow/NotesPanel.tsx
Comment thread apps/frontend/src/components/escrow/views/EscrowPendingView.tsx Outdated
Comment thread apps/frontend/src/components/escrow/views/EscrowStatusView.tsx Outdated
@Jonatan-Chaverri Jonatan-Chaverri force-pushed the feat/add-escrow-details-page branch from cd65111 to 2bec3b6 Compare June 19, 2026 04:19
@sotoJ24 sotoJ24 self-requested a review June 23, 2026 00:37

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

well done @Jonatan-Chaverri

@sotoJ24 sotoJ24 merged commit 27720aa into safetrustcr:main Jun 23, 2026
1 check 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.

feat: build escrow detail page that renders the correct view based on escrow status

2 participants