-
Notifications
You must be signed in to change notification settings - Fork 24
πfeat: scaffold apps/web/src/app/hotel/[id]/escrow/[escrowId]/ waterfall route placeholderΒ #72
Copy link
Copy link
Open
Labels
Description
Issue Summary
Create the dynamic escrow detail route /hotel/[id]/escrow/[escrowId] in
apps/web/ with a status-driven stub that renders one of four view placeholders
based on a status query param. This is the final step of the waterfall β
the single URL that adapts to show Paid / Deposit blocked / Deposit released.
Type of Issue
- Feature Request
Depends on
Issue #70 (escrow create) β successful signing navigates here.
Files to create
apps/web/src/app/hotel/[id]/escrow/
βββ [escrowId]/
βββ page.tsx
apps/web/src/app/hotel/[id]/escrow/[escrowId]/page.tsx
// TODO: replace stub views with real components once merged in frontend-SafeTrust
// Sources:
// frontend-SafeTrust/src/components/escrow/views/EscrowPaidView.tsx
// frontend-SafeTrust/src/components/escrow/views/EscrowBlockedView.tsx
// frontend-SafeTrust/src/components/escrow/views/EscrowReleasedView.tsx
// frontend-SafeTrust/src/components/escrow/RealTimeEscrowStatus.tsx
//
// Status-to-view mapping (when wired):
// funded β EscrowPaidView (Payment batch)
// active β EscrowBlockedView (Deposit blocked)
// completed β EscrowReleasedView (Deposit released)
// default β redirect to /hotel/[id]/escrow/create
//
// Real-time: RealTimeEscrowStatus (Hasura subscription) drives automatic transitions
import { ProcessStepper } from '@/components/escrow/ProcessStepper';
import { InvoiceHeader } from '@/components/escrow/InvoiceHeader';
// Dev helper: pass ?status=paid|blocked|released to test each view
function getStubView(status: string) {
switch (status) {
case 'paid': return { label: 'paid', step: 2 as const, title: 'Payment batch January 2025' };
case 'blocked': return { label: 'blocked', step: 3 as const, title: 'Payment batch - Escrow Status' };
case 'released': return { label: 'released', step: 4 as const, title: 'Deposit / Escrow Released' };
default: return { label: 'paid', step: 2 as const, title: 'Payment batch January 2025' };
}
}
export default function EscrowDetailPage({
params,
searchParams,
}: {
params: { id: string; escrowId: string };
searchParams: { status?: string };
}) {
const status = searchParams?.status ?? 'paid';
const view = getStubView(status);
return (
<div className="max-w-5xl mx-auto p-6">
<InvoiceHeader
invoiceNumber="INV4257-09-012"
status={view.label as 'paid' | 'blocked' | 'released'}
paidAt="25 Jan 2025"
/>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mt-6">
{/* Left β view content */}
<div className="lg:col-span-2 border rounded-lg p-6 bg-card space-y-6">
<h2 className="text-xl font-bold">{view.title}</h2>
{/* TODO: replace with real view component based on status */}
{status === 'paid' && (
<div className="space-y-4">
{/* TODO: EscrowPaidView β billing info, invoice table, terms */}
<div className="grid grid-cols-2 gap-4 text-sm">
<div><p className="text-muted-foreground">Billed to</p><p className="font-medium">John_s@gmail.com</p></div>
<div><p className="text-muted-foreground">Invoice Number</p><p className="font-medium">INV4257-09-012</p></div>
<div><p className="text-muted-foreground">Billing details</p><p className="font-medium">John Smith</p></div>
<div><p className="text-muted-foreground">Currency</p><p className="font-medium">IDR - Dollar</p></div>
</div>
<div className="border rounded overflow-hidden">
<table className="w-full text-sm">
<thead className="bg-muted">
<tr>
<th className="text-left p-3">PRODUCT</th>
<th className="text-right p-3">PRICE / MONTH</th>
<th className="text-right p-3">DEPOSIT</th>
</tr>
</thead>
<tbody>
<tr className="border-t">
<td className="p-3">La sabana apartment</td>
<td className="text-right p-3">$4,000</td>
<td className="text-right p-3">$4,000</td>
</tr>
</tbody>
</table>
</div>
<div className="text-sm"><span className="font-semibold">Total: $8,000</span></div>
</div>
)}
{status === 'blocked' && (
<div className="space-y-4">
{/* TODO: EscrowBlockedView β escrow description, tenant/owner info */}
<div className="text-sm space-y-1">
<p><span className="text-muted-foreground">Creation date</span> <span className="font-medium ml-2">25 January 2025</span></p>
<p><span className="text-muted-foreground">Amount blocked</span> <span className="font-medium ml-2">$4,000</span></p>
</div>
<div className="grid grid-cols-2 gap-6 border-t pt-4 text-sm">
<div>
<p className="font-semibold mb-2">Tenant Information</p>
<p className="text-muted-foreground">Tenant name <span className="text-foreground ml-2">John Smith</span></p>
<p className="text-muted-foreground">Wallet Address <span className="text-foreground ml-2">MJE...XN32</span></p>
<p className="text-muted-foreground">Email <span className="text-foreground ml-2">John_s@gmail.com</span></p>
</div>
<div>
<p className="font-semibold mb-2">Owner Information</p>
<p className="text-muted-foreground">Owner name <span className="text-foreground ml-2">Alberto Casas</span></p>
<p className="text-muted-foreground">Wallet Address <span className="text-foreground ml-2">MJE...XN32</span></p>
<p className="text-muted-foreground">Email <span className="text-foreground ml-2">albertoCasas100@gmail.com</span></p>
</div>
</div>
</div>
)}
{status === 'released' && (
<div className="space-y-4">
{/* TODO: EscrowReleasedView β justification, beneficiary, claims */}
<div>
<div className="flex items-center justify-between mb-2">
<h3 className="font-semibold">Escrow Justification</h3>
<button disabled className="text-xs border px-2 py-1 rounded opacity-50">π PDF</button>
</div>
<textarea className="w-full border rounded p-2 text-sm h-24 resize-none" placeholder="Justification..." />
</div>
<div className="grid grid-cols-2 gap-6 text-sm">
<div>
<p className="font-semibold mb-2">Beneficiary Information</p>
<p className="text-muted-foreground">Name <span className="text-foreground ml-2">John Smith</span></p>
<p className="text-muted-foreground">Wallet <span className="text-foreground ml-2">MJE...XN32</span></p>
<p className="text-muted-foreground">Released date <span className="text-foreground ml-2">20 January 2025</span></p>
<p className="text-muted-foreground">Deposit <span className="text-foreground ml-2">$4,000</span></p>
</div>
<div>
<p className="font-semibold mb-2">Claims</p>
<textarea className="w-full border rounded p-2 text-sm h-20 resize-none" placeholder="Claims..." />
<div className="flex gap-2 mt-2">
<button className="px-4 py-1 border rounded text-sm">Clean</button>
<button className="px-4 py-1 bg-orange-500 text-white rounded text-sm">Send</button>
</div>
</div>
</div>
</div>
)}
</div>
{/* Right β Notes + Process stepper */}
<div className="space-y-4">
<div className="border rounded-lg p-4 bg-card">
<h3 className="font-semibold mb-2">Notes</h3>
<textarea className="w-full text-sm border rounded p-2 h-24 resize-none" placeholder="Notes..." />
</div>
<ProcessStepper currentStep={view.step} />
</div>
</div>
{/* Dev helper hint */}
<p className="text-xs text-muted-foreground mt-4">
Dev: append <code>?status=paid</code>, <code>?status=blocked</code>, or
<code>?status=released</code> to preview each view state.
</p>
</div>
);
}Acceptance Criteria
-
apps/web/src/app/hotel/[id]/escrow/[escrowId]/page.tsxexists -
GET /hotel/1/escrow/abc-123?status=paidrenders Payment batch view -
GET /hotel/1/escrow/abc-123?status=blockedrenders Deposit blocked view -
GET /hotel/1/escrow/abc-123?status=releasedrenders Deposit released view -
ProcessSteppershows correct active steps per status (2, 3, 4 respectively) -
InvoiceHeadershows correct badge color per status - Dev hint paragraph visible at bottom
- All TODO comments reference correct source files and the real-time
subscription path (RealTimeEscrowStatus) -
pnpm build --filter @safetrust/webpasses
References
Reactions are currently unavailable