Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 41 additions & 65 deletions apps/frontend/app/escrow/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useEffect, useState } from "react";
import { useEffect, useState, useCallback } from "react";
import { useParams } from "next/navigation";
import Link from "next/link";
import { useEscrow } from "@/hooks/useEscrow";
Expand All @@ -19,11 +19,10 @@ import { EscrowDetailSkeleton } from "@/components/ui/EscrowDetailSkeleton";

const EscrowDetailPage = () => {
const { id } = useParams();
const { escrow, error, loading, refetch } = useEscrow(id as string);
const { escrow, error, loading, refetch, isLive } = useEscrow(id as string);
const { connected, publicKey, connect } = useWallet();
const [userRole, setUserRole] = useState<
"creator" | "counterparty" | "arbitrator" | null
>(null);

const [userRole, setUserRole] = useState<"creator" | "counterparty" | "arbitrator" | null>(null);
const [currentParty, setCurrentParty] = useState<IParty | null>(null);
const [disputeOpen, setDisputeOpen] = useState(false);
const [resolutionOpen, setResolutionOpen] = useState(false);
Expand All @@ -49,37 +48,34 @@ const EscrowDetailPage = () => {
}
}, [escrow, publicKey]);

// Fetch dispute data when escrow is in DISPUTED status
useEffect(() => {
const fetchDispute = async () => {
if (escrow?.status !== "DISPUTED") {
setDispute(null);
return;
}

try {
const response = await fetch(`/api/escrows/${escrow.id}/dispute`);
if (response.ok) {
const disputeData = await response.json();
setDispute(disputeData);
}
} catch (error) {
console.error("Error fetching dispute:", error);
const fetchDisputeData = useCallback(async () => {
if (escrow?.status !== "DISPUTED") {
setDispute(null);
return;
}
try {
const response = await fetch(`/api/escrows/${escrow.id}/dispute`);
if (response.ok) {
const disputeData = await response.json();
setDispute(disputeData);
}
};

fetchDispute();
} catch (error) {
console.error("Error fetching dispute details:", error);
}
}, [escrow?.id, escrow?.status]);

// Hook up dispute monitoring states
useEffect(() => {
void fetchDisputeData();
}, [fetchDisputeData]);

if (loading) return <EscrowDetailSkeleton />;

if (error) {
return (
<div className="min-h-screen flex items-center justify-center bg-background text-foreground p-4">
<div className="bg-card text-card-foreground p-6 sm:p-8 rounded-xl shadow-sm border border-border max-w-md w-full text-center">
<h2 className="text-xl font-bold text-destructive mb-3">
Error Loading Escrow
</h2>
<h2 className="text-xl font-bold text-destructive mb-3">Error Loading Escrow</h2>
<p className="text-muted-foreground text-sm mb-4">{error}</p>
<button
onClick={() => window.location.reload()}
Expand All @@ -96,12 +92,8 @@ const EscrowDetailPage = () => {
return (
<div className="min-h-screen flex items-center justify-center bg-background text-foreground p-4">
<div className="bg-card text-card-foreground p-6 sm:p-8 rounded-xl shadow-sm border border-border max-w-md w-full text-center">
<h2 className="text-xl font-bold text-foreground mb-3">
Escrow Not Found
</h2>
<p className="text-muted-foreground text-sm mb-4">
The requested escrow agreement could not be found.
</p>
<h2 className="text-xl font-bold text-foreground mb-3">Escrow Not Found</h2>
<p className="text-muted-foreground text-sm mb-4">The requested escrow agreement could not be found.</p>
<Link
href="/escrow"
className="min-h-11 inline-flex items-center px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium transition-colors cursor-pointer"
Expand All @@ -116,6 +108,19 @@ const EscrowDetailPage = () => {
return (
<div className="min-h-screen bg-background text-foreground py-4 sm:py-8">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">

{/* Visual Live Status Sync Badge Indicator */}
<div className="flex justify-end mb-3">
<div className={`inline-flex items-center gap-2 px-3 py-1 rounded-full text-xs font-semibold border transition-all ${
isLive
? "bg-emerald-500/10 text-emerald-500 border-emerald-500/30"
: "bg-amber-500/10 text-amber-500 border-amber-500/30 animate-pulse"
}`}>
<span className={`h-2 w-2 rounded-full ${isLive ? "bg-emerald-500 animate-ping" : "bg-amber-500"}`} />
{isLive ? "LIVE SYNC ACTIVE" : "DISCONNECTED — POLLING MODE"}
</div>
</div>

<EscrowHeader
escrow={escrow}
userRole={userRole}
Expand All @@ -125,13 +130,11 @@ const EscrowDetailPage = () => {
onFileDispute={() => setDisputeOpen(true)}
/>

{/* On mobile: Terms card sits below header, before the main content columns */}
<div className="lg:hidden mt-4">
<TermsSection escrow={escrow} userRole={userRole} />
</div>

<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6 mt-4 sm:mt-8">
{/* Main content column */}
<div className="lg:col-span-2 space-y-4 sm:space-y-6">
{escrow.status === "DISPUTED" && (
<DisputeSection
Expand All @@ -142,21 +145,7 @@ const EscrowDetailPage = () => {
dispute={dispute}
onDisputeUpdate={() => {
refetch();
// Refetch dispute data
const fetchDispute = async () => {
try {
const response = await fetch(
`/api/escrows/${escrow.id}/dispute`,
);
if (response.ok) {
const disputeData = await response.json();
setDispute(disputeData);
}
} catch (error) {
console.error("Error fetching dispute:", error);
}
};
fetchDispute();
void fetchDisputeData();
}}
onResolveDispute={() => setResolutionOpen(true)}
/>
Expand All @@ -178,7 +167,6 @@ const EscrowDetailPage = () => {
<ActivityFeed escrowId={id as string} />
</div>

{/* Sidebar — hidden on mobile (shown above) */}
<div className="hidden lg:block lg:col-span-1">
<TermsSection escrow={escrow} userRole={userRole} />
</div>
Expand All @@ -193,19 +181,7 @@ const EscrowDetailPage = () => {
escrowStatus={escrow.status}
onDisputeUpdate={() => {
refetch();
// Fetch the newly created dispute
const fetchDispute = async () => {
try {
const response = await fetch(`/api/escrows/${escrow.id}/dispute`);
if (response.ok) {
const disputeData = await response.json();
setDispute(disputeData);
}
} catch (error) {
console.error("Error fetching dispute:", error);
}
};
fetchDispute();
void fetchDisputeData();
}}
/>
<ArbitratorResolutionModal
Expand All @@ -223,4 +199,4 @@ const EscrowDetailPage = () => {
);
};

export default EscrowDetailPage;
export default EscrowDetailPage;
Loading
Loading