Skip to content

πŸ“Œfeat: scaffold apps/web/src/app/hotel/[id]/escrow/[escrowId]/ waterfall route placeholderΒ #72

@sotoJ24

Description

@sotoJ24

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.tsx exists
  • GET /hotel/1/escrow/abc-123?status=paid renders Payment batch view
  • GET /hotel/1/escrow/abc-123?status=blocked renders Deposit blocked view
  • GET /hotel/1/escrow/abc-123?status=released renders Deposit released view
  • ProcessStepper shows correct active steps per status (2, 3, 4 respectively)
  • InvoiceHeader shows 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/web passes

References


Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions