From 2b4179cf49eda8e343bcabfa949249c4b9d620dd Mon Sep 17 00:00:00 2001 From: shamoo53 Date: Fri, 30 Jan 2026 16:50:53 +0100 Subject: [PATCH 1/2] implemented the Escrow detail page --- apps/frontend/app/escrow/[id]/page.tsx | 110 ++++++++++++++ .../components/escrow/detail/EscrowHeader.tsx | 110 ++++++++++++++ .../escrow/detail/PartiesSection.tsx | 94 ++++++++++++ .../components/escrow/detail/TermsSection.tsx | 102 +++++++++++++ .../escrow/detail/TimelineSection.tsx | 142 ++++++++++++++++++ .../escrow/detail/TransactionHistory.tsx | 94 ++++++++++++ apps/frontend/hooks/useEscrow.ts | 41 +++++ apps/frontend/hooks/useWallet.ts | 19 +++ apps/frontend/package-lock.json | 10 -- apps/frontend/types/escrow.d.ts | 58 +++++++ 10 files changed, 770 insertions(+), 10 deletions(-) create mode 100644 apps/frontend/app/escrow/[id]/page.tsx create mode 100644 apps/frontend/components/escrow/detail/EscrowHeader.tsx create mode 100644 apps/frontend/components/escrow/detail/PartiesSection.tsx create mode 100644 apps/frontend/components/escrow/detail/TermsSection.tsx create mode 100644 apps/frontend/components/escrow/detail/TimelineSection.tsx create mode 100644 apps/frontend/components/escrow/detail/TransactionHistory.tsx create mode 100644 apps/frontend/hooks/useEscrow.ts create mode 100644 apps/frontend/hooks/useWallet.ts create mode 100644 apps/frontend/types/escrow.d.ts diff --git a/apps/frontend/app/escrow/[id]/page.tsx b/apps/frontend/app/escrow/[id]/page.tsx new file mode 100644 index 0000000..4a3ba08 --- /dev/null +++ b/apps/frontend/app/escrow/[id]/page.tsx @@ -0,0 +1,110 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useParams } from 'next/navigation'; +import Link from 'next/link'; +import { useEscrow } from '@/hooks/useEscrow'; +import { useWallet } from '@/hooks/useWallet'; +import EscrowHeader from '@/components/escrow/detail/EscrowHeader'; +import PartiesSection from '@/components/escrow/detail/PartiesSection'; +import TermsSection from '@/components/escrow/detail/TermsSection'; +import TimelineSection from '@/components/escrow/detail/TimelineSection'; +import TransactionHistory from '@/components/escrow/detail/TransactionHistory'; +import { Escrow } from '@/types/escrow'; + +const EscrowDetailPage = () => { + const { id } = useParams(); + const { escrow, loading, error } = useEscrow(id as string); + const { connected, publicKey, connect } = useWallet(); // Assuming wallet hook exists + const [userRole, setUserRole] = useState<'creator' | 'counterparty' | null>(null); + + useEffect(() => { + if (escrow && publicKey) { + if (escrow.creatorId === publicKey) { + setUserRole('creator'); + } else if (escrow.parties?.some((party: any) => party.userId === publicKey)) { + setUserRole('counterparty'); + } + } + }, [escrow, publicKey]); + + if (loading) { + return ( +
+
+
+

Loading escrow details...

+
+
+ ); + } + + if (error) { + return ( +
+
+

Error Loading Escrow

+

{error}

+ +
+
+ ); + } + + if (!escrow) { + return ( +
+
+

Escrow Not Found

+

The requested escrow agreement could not be found.

+ + Back to Escrows + +
+
+ ); + } + + return ( +
+
+ {/* Header Section */} + + +
+
+ {/* Parties Section */} + + + {/* Timeline Section */} + + + {/* Transaction History */} + +
+ +
+ {/* Terms Section */} + +
+
+
+
+ ); +}; + +export default EscrowDetailPage; \ No newline at end of file diff --git a/apps/frontend/components/escrow/detail/EscrowHeader.tsx b/apps/frontend/components/escrow/detail/EscrowHeader.tsx new file mode 100644 index 0000000..366cce4 --- /dev/null +++ b/apps/frontend/components/escrow/detail/EscrowHeader.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import { Share, Copy, Wallet, Clock, CheckCircle, AlertTriangle, XCircle, ShareIcon } from 'lucide-react'; +import { Escrow } from '@/types/escrow'; + +interface EscrowHeaderProps { + escrow: Escrow; + userRole: 'creator' | 'counterparty' | null; + connected: boolean; + connect: () => void; + publicKey: string | null; +} + +const getStatusColor = (status: string) => { + switch (status.toLowerCase()) { + case 'pending': + return 'bg-yellow-100 text-yellow-800'; + case 'active': + return 'bg-blue-100 text-blue-800'; + case 'completed': + return 'bg-green-100 text-green-800'; + case 'cancelled': + return 'bg-gray-100 text-gray-800'; + case 'disputed': + return 'bg-red-100 text-red-800'; + default: + return 'bg-gray-100 text-gray-800'; + } +}; + +const getStatusIcon = (status: string) => { + switch (status.toLowerCase()) { + case 'pending': + return ; + case 'active': + return ; + case 'completed': + return ; + case 'cancelled': + return ; + case 'disputed': + return ; + default: + return ; + } +}; + +const EscrowHeader: React.FC = ({ + escrow, + userRole, + connected, + connect, + publicKey +}: EscrowHeaderProps) => { + const handleCopyLink = () => { + navigator.clipboard.writeText(window.location.href); + alert('Link copied to clipboard!'); + }; + + return ( +
+
+
+
+

{escrow.title}

+ + {getStatusIcon(escrow.status)} + {escrow.status} + +
+

{escrow.description}

+ +
+
+ ID: + {escrow.id.substring(0, 8)}... +
+
+ Amount: + {Number(escrow.amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 7 })} {escrow.asset} +
+
+ Created: + {new Date(escrow.createdAt).toLocaleDateString()} +
+
+
+ +
+ + {!connected && ( + + )} +
+
+
+ ); +}; + +export default EscrowHeader; \ No newline at end of file diff --git a/apps/frontend/components/escrow/detail/PartiesSection.tsx b/apps/frontend/components/escrow/detail/PartiesSection.tsx new file mode 100644 index 0000000..2d45e8e --- /dev/null +++ b/apps/frontend/components/escrow/detail/PartiesSection.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { Escrow } from '@/types/escrow'; + +interface PartiesSectionProps { + escrow: Escrow; + userRole: 'creator' | 'counterparty' | null; +} + +const getRoleColor = (role: string) => { + switch (role.toUpperCase()) { + case 'BUYER': + return 'bg-blue-100 text-blue-800'; + case 'SELLER': + return 'bg-green-100 text-green-800'; + case 'ARBITRATOR': + return 'bg-purple-100 text-purple-800'; + default: + return 'bg-gray-100 text-gray-800'; + } +}; + +const getStatusColor = (status: string) => { + switch (status.toUpperCase()) { + case 'ACCEPTED': + return 'bg-green-100 text-green-800'; + case 'REJECTED': + return 'bg-red-100 text-red-800'; + case 'PENDING': + return 'bg-yellow-100 text-yellow-800'; + default: + return 'bg-gray-100 text-gray-800'; + } +}; + +const PartiesSection: React.FC = ({ escrow, userRole }: PartiesSectionProps) => { + return ( +
+

Parties

+ +
+ {escrow.parties.map((party) => ( +
+
+
+
+ + {party.role} + + + {party.status} + +
+

+ User ID: {party.userId} +

+
+ + {userRole === 'creator' && party.status === 'PENDING' && ( +
+ + +
+ )} +
+
+ ))} +
+ + {escrow.conditions && escrow.conditions.length > 0 && ( +
+

Conditions

+
    + {escrow.conditions.map((condition) => ( +
  • +
    + + + +
    +

    {condition.description}

    +
  • + ))} +
+
+ )} +
+ ); +}; + +export default PartiesSection; \ No newline at end of file diff --git a/apps/frontend/components/escrow/detail/TermsSection.tsx b/apps/frontend/components/escrow/detail/TermsSection.tsx new file mode 100644 index 0000000..8975d75 --- /dev/null +++ b/apps/frontend/components/escrow/detail/TermsSection.tsx @@ -0,0 +1,102 @@ +import React, { useState, useEffect } from 'react'; +import { Escrow } from '@/types/escrow'; + +interface TermsSectionProps { + escrow: Escrow; + userRole: 'creator' | 'counterparty' | null; +} + +const TermsSection: React.FC = ({ escrow, userRole }: TermsSectionProps) => { + const [timeLeft, setTimeLeft] = useState(''); + + useEffect(() => { + if (escrow.expiresAt) { + const calculateTimeLeft = () => { + const expiryDate = new Date(escrow.expiresAt!); + const now = new Date(); + const difference = expiryDate.getTime() - now.getTime(); + + if (difference <= 0) { + return 'Expired'; + } + + const days = Math.floor(difference / (1000 * 60 * 60 * 24)); + const hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + const minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)); + + return `${days}d ${hours}h ${minutes}m`; + }; + + setTimeLeft(calculateTimeLeft()); + const timer = setInterval(() => { + setTimeLeft(calculateTimeLeft()); + }, 60000); // Update every minute + + return () => clearInterval(timer); + } + }, [escrow.expiresAt]); + + return ( +
+

Terms & Actions

+ +
+
+

Agreement Details

+
+
+
Amount
+
{Number(escrow.amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 7 })} {escrow.asset}
+
+
+
Type
+
{escrow.type}
+
+
+
Status
+
{escrow.status}
+
+ {escrow.expiresAt && ( +
+
Expires
+
+ {new Date(escrow.expiresAt).toLocaleDateString()} +
+ {timeLeft} +
+
+ )} +
+
+ + {/* Action buttons based on user role and escrow status */} +
+ {userRole === 'creator' && escrow.status === 'PENDING' && ( + + )} + + {userRole === 'counterparty' && escrow.status === 'ACTIVE' && ( + <> + + + + )} + + {(userRole === 'creator' || userRole === 'counterparty') && escrow.status === 'ACTIVE' && ( + + )} +
+
+
+ ); +}; + +export default TermsSection; \ No newline at end of file diff --git a/apps/frontend/components/escrow/detail/TimelineSection.tsx b/apps/frontend/components/escrow/detail/TimelineSection.tsx new file mode 100644 index 0000000..8d24608 --- /dev/null +++ b/apps/frontend/components/escrow/detail/TimelineSection.tsx @@ -0,0 +1,142 @@ +import React from 'react'; +import { Escrow } from '@/types/escrow'; + +interface TimelineSectionProps { + escrow: Escrow; +} + +const getEventIcon = (eventType: string) => { + switch (eventType.toUpperCase()) { + case 'CREATED': + return ( + + + + + + ); + case 'FUNDED': + return ( + + + + + + ); + case 'CONDITION_MET': + return ( + + + + + + ); + case 'COMPLETED': + return ( + + + + + + ); + case 'CANCELLED': + return ( + + + + + + ); + default: + return ( + + + + + + ); + } +}; + +const getEventColor = (eventType: string) => { + switch (eventType.toUpperCase()) { + case 'CREATED': + return 'bg-blue-50 text-blue-700'; + case 'FUNDED': + return 'bg-green-50 text-green-700'; + case 'CONDITION_MET': + return 'bg-yellow-50 text-yellow-700'; + case 'COMPLETED': + return 'bg-purple-50 text-purple-700'; + case 'CANCELLED': + return 'bg-red-50 text-red-700'; + default: + return 'bg-gray-50 text-gray-700'; + } +}; + +const TimelineSection: React.FC = ({ escrow }: TimelineSectionProps) => { + // Sort events by date, with the newest first + const sortedEvents = [...escrow.events].sort((a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + ); + + return ( +
+

Timeline

+ + {sortedEvents.length === 0 ? ( +

No events recorded yet.

+ ) : ( +
+
    + {sortedEvents.map((event, index) => ( +
  • +
    + {index !== sortedEvents.length - 1 ? ( +
    +
  • + ))} +
+
+ )} +
+ ); +}; + +export default TimelineSection; \ No newline at end of file diff --git a/apps/frontend/components/escrow/detail/TransactionHistory.tsx b/apps/frontend/components/escrow/detail/TransactionHistory.tsx new file mode 100644 index 0000000..f1b1d67 --- /dev/null +++ b/apps/frontend/components/escrow/detail/TransactionHistory.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { Escrow } from '@/types/escrow'; + +interface TransactionHistoryProps { + escrow: Escrow; +} + +const TransactionHistory: React.FC = ({ escrow }: TransactionHistoryProps) => { + // Filter events that represent transactions + const transactionEvents = escrow.events.filter(event => + event.eventType === 'FUNDED' || + event.eventType === 'CONDITION_MET' || + event.eventType === 'COMPLETED' || + event.eventType === 'CANCELLED' + ); + + return ( +
+

Transaction History

+ + {transactionEvents.length === 0 ? ( +

No transaction history available.

+ ) : ( +
+ + + + + + + + + + + + {transactionEvents.map((event) => ( + + + + + + + + ))} + +
+ Type + + Amount + + Status + + Date + + Transaction +
+
+ {event.eventType.replace(/_/g, ' ').toLowerCase()} +
+
+
+ {event.eventType === 'FUNDED' && `${Number(escrow.amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 7 })} ${escrow.asset}`} + {event.eventType === 'COMPLETED' && `${Number(escrow.amount).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 7 })} ${escrow.asset}`} + {event.eventType === 'CANCELLED' && '0 XLM'} + {event.eventType === 'CONDITION_MET' && 'Variable'} +
+
+ + {event.eventType === 'COMPLETED' ? 'Completed' : + event.eventType === 'CANCELLED' ? 'Cancelled' : 'Processed'} + + + {new Date(event.createdAt).toLocaleDateString()} + + {event.data?.transactionHash ? ( + + View Transaction + + ) : ( + N/A + )} +
+
+ )} +
+ ); +}; + +export default TransactionHistory; \ No newline at end of file diff --git a/apps/frontend/hooks/useEscrow.ts b/apps/frontend/hooks/useEscrow.ts new file mode 100644 index 0000000..54d1565 --- /dev/null +++ b/apps/frontend/hooks/useEscrow.ts @@ -0,0 +1,41 @@ +import { useState, useEffect } from 'react'; +import { Escrow, UseEscrowReturn } from '@/types/escrow'; + +export const useEscrow = (id: string): UseEscrowReturn => { + const [escrow, setEscrow] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchEscrow = async () => { + try { + setLoading(true); + const response = await fetch(`/api/escrows/${id}`); + + if (!response.ok) { + if (response.status === 404) { + setError('Escrow not found'); + } else { + setError('Failed to load escrow details'); + } + return; + } + + const data = await response.json(); + setEscrow(data); + setError(null); + } catch (err) { + setError('An error occurred while fetching escrow details'); + console.error('Error fetching escrow:', err); + } finally { + setLoading(false); + } + }; + + if (id) { + fetchEscrow(); + } + }, [id]); + + return { escrow, loading, error }; +}; \ No newline at end of file diff --git a/apps/frontend/hooks/useWallet.ts b/apps/frontend/hooks/useWallet.ts new file mode 100644 index 0000000..b1f849b --- /dev/null +++ b/apps/frontend/hooks/useWallet.ts @@ -0,0 +1,19 @@ +import { useState } from 'react'; +import { WalletHookReturn } from '@/types/escrow'; + +export const useWallet = (): WalletHookReturn => { + const [connected, setConnected] = useState(false); + const [publicKey, setPublicKey] = useState(null); + + const connect = () => { + // Mock implementation - in real app this would connect to a wallet provider + setConnected(true); + setPublicKey('GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H'); // Mock public key + }; + + return { + connected, + publicKey, + connect + }; +}; \ No newline at end of file diff --git a/apps/frontend/package-lock.json b/apps/frontend/package-lock.json index 02fc5af..4c2e3ba 100644 --- a/apps/frontend/package-lock.json +++ b/apps/frontend/package-lock.json @@ -1294,7 +1294,6 @@ "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1354,7 +1353,6 @@ "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -1861,7 +1859,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2856,7 +2853,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -3026,7 +3022,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -5388,7 +5383,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5398,7 +5392,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -5411,7 +5404,6 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.1.tgz", "integrity": "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -6223,7 +6215,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -6424,7 +6415,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/apps/frontend/types/escrow.d.ts b/apps/frontend/types/escrow.d.ts new file mode 100644 index 0000000..50cfa42 --- /dev/null +++ b/apps/frontend/types/escrow.d.ts @@ -0,0 +1,58 @@ +// Define types based on backend entities +export interface Party { + id: string; + userId: string; + role: 'BUYER' | 'SELLER' | 'ARBITRATOR'; + status: 'PENDING' | 'ACCEPTED' | 'REJECTED'; + createdAt: string; +} + +export interface Condition { + id: string; + description: string; + type: string; + metadata?: Record; +} + +export interface EscrowEvent { + id: string; + eventType: 'CREATED' | 'PARTY_ADDED' | 'PARTY_ACCEPTED' | 'PARTY_REJECTED' | 'FUNDED' | 'CONDITION_MET' | 'STATUS_CHANGED' | 'UPDATED' | 'CANCELLED' | 'COMPLETED' | 'DISPUTED'; + actorId?: string; + data?: Record; + ipAddress?: string; + createdAt: string; +} + +export interface Escrow { + id: string; + title: string; + description?: string; + amount: number; + asset: string; + status: 'PENDING' | 'ACTIVE' | 'COMPLETED' | 'CANCELLED' | 'DISPUTED'; + type: 'STANDARD' | 'MILESTONE' | 'TIMED'; + creatorId: string; + expiresAt?: string; + isActive: boolean; + creator: { + id: string; + walletAddress?: string; + }; + parties: Party[]; + conditions: Condition[]; + events: EscrowEvent[]; + createdAt: string; + updatedAt: string; +} + +export interface UseEscrowReturn { + escrow: Escrow | null; + loading: boolean; + error: string | null; +} + +export interface WalletHookReturn { + connected: boolean; + publicKey: string | null; + connect: () => void; +} \ No newline at end of file From c3eb7bd2ae6466b50b8eec9fd8d4360284d7ed5a Mon Sep 17 00:00:00 2001 From: shamoo53 Date: Fri, 30 Jan 2026 20:44:50 +0100 Subject: [PATCH 2/2] fixes --- apps/frontend/app/escrow/[id]/page.tsx | 2 +- apps/frontend/app/page.tsx | 23 ++++---- .../components/escrow/detail/EscrowHeader.tsx | 4 +- .../escrow/detail/PartiesSection.tsx | 8 +-- .../components/escrow/detail/TermsSection.tsx | 4 +- .../escrow/detail/TimelineSection.tsx | 4 +- .../escrow/detail/TransactionHistory.tsx | 8 +-- apps/frontend/hooks/useEscrow.ts | 6 +- apps/frontend/hooks/useWallet.ts | 4 +- apps/frontend/types/escrow.d.ts | 58 ------------------- apps/frontend/types/escrow.ts | 53 ++++++++++++++++- 11 files changed, 84 insertions(+), 90 deletions(-) delete mode 100644 apps/frontend/types/escrow.d.ts diff --git a/apps/frontend/app/escrow/[id]/page.tsx b/apps/frontend/app/escrow/[id]/page.tsx index 4a3ba08..ad72f70 100644 --- a/apps/frontend/app/escrow/[id]/page.tsx +++ b/apps/frontend/app/escrow/[id]/page.tsx @@ -10,7 +10,7 @@ import PartiesSection from '@/components/escrow/detail/PartiesSection'; import TermsSection from '@/components/escrow/detail/TermsSection'; import TimelineSection from '@/components/escrow/detail/TimelineSection'; import TransactionHistory from '@/components/escrow/detail/TransactionHistory'; -import { Escrow } from '@/types/escrow'; +import { IEscrowExtended } from '@/types/escrow'; const EscrowDetailPage = () => { const { id } = useParams(); diff --git a/apps/frontend/app/page.tsx b/apps/frontend/app/page.tsx index 432c880..da685af 100644 --- a/apps/frontend/app/page.tsx +++ b/apps/frontend/app/page.tsx @@ -1,4 +1,5 @@ -import Image from "next/image"; + import Image from "next/image"; + import Link from "next/link"; export default function Home() { return ( @@ -27,18 +28,18 @@ export default function Home() {

@@ -64,26 +65,26 @@ export default function Home() { ); diff --git a/apps/frontend/components/escrow/detail/EscrowHeader.tsx b/apps/frontend/components/escrow/detail/EscrowHeader.tsx index 366cce4..b320136 100644 --- a/apps/frontend/components/escrow/detail/EscrowHeader.tsx +++ b/apps/frontend/components/escrow/detail/EscrowHeader.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { Share, Copy, Wallet, Clock, CheckCircle, AlertTriangle, XCircle, ShareIcon } from 'lucide-react'; -import { Escrow } from '@/types/escrow'; +import { IEscrowExtended } from '@/types/escrow'; interface EscrowHeaderProps { - escrow: Escrow; + escrow: IEscrowExtended; userRole: 'creator' | 'counterparty' | null; connected: boolean; connect: () => void; diff --git a/apps/frontend/components/escrow/detail/PartiesSection.tsx b/apps/frontend/components/escrow/detail/PartiesSection.tsx index 2d45e8e..730682d 100644 --- a/apps/frontend/components/escrow/detail/PartiesSection.tsx +++ b/apps/frontend/components/escrow/detail/PartiesSection.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import { Escrow } from '@/types/escrow'; +import { IEscrowExtended } from '@/types/escrow'; interface PartiesSectionProps { - escrow: Escrow; + escrow: IEscrowExtended; userRole: 'creator' | 'counterparty' | null; } @@ -38,7 +38,7 @@ const PartiesSection: React.FC = ({ escrow, userRole }: Par

Parties

- {escrow.parties.map((party) => ( + {escrow.parties.map((party: any) => (
@@ -74,7 +74,7 @@ const PartiesSection: React.FC = ({ escrow, userRole }: Par

Conditions

    - {escrow.conditions.map((condition) => ( + {escrow.conditions.map((condition: any) => (
  • diff --git a/apps/frontend/components/escrow/detail/TermsSection.tsx b/apps/frontend/components/escrow/detail/TermsSection.tsx index 8975d75..fdcd16f 100644 --- a/apps/frontend/components/escrow/detail/TermsSection.tsx +++ b/apps/frontend/components/escrow/detail/TermsSection.tsx @@ -1,8 +1,8 @@ import React, { useState, useEffect } from 'react'; -import { Escrow } from '@/types/escrow'; +import { IEscrowExtended } from '@/types/escrow'; interface TermsSectionProps { - escrow: Escrow; + escrow: IEscrowExtended; userRole: 'creator' | 'counterparty' | null; } diff --git a/apps/frontend/components/escrow/detail/TimelineSection.tsx b/apps/frontend/components/escrow/detail/TimelineSection.tsx index 8d24608..c1de3d5 100644 --- a/apps/frontend/components/escrow/detail/TimelineSection.tsx +++ b/apps/frontend/components/escrow/detail/TimelineSection.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import { Escrow } from '@/types/escrow'; +import { IEscrowExtended } from '@/types/escrow'; interface TimelineSectionProps { - escrow: Escrow; + escrow: IEscrowExtended; } const getEventIcon = (eventType: string) => { diff --git a/apps/frontend/components/escrow/detail/TransactionHistory.tsx b/apps/frontend/components/escrow/detail/TransactionHistory.tsx index f1b1d67..add7469 100644 --- a/apps/frontend/components/escrow/detail/TransactionHistory.tsx +++ b/apps/frontend/components/escrow/detail/TransactionHistory.tsx @@ -1,13 +1,13 @@ import React from 'react'; -import { Escrow } from '@/types/escrow'; +import { IEscrowExtended } from '@/types/escrow'; interface TransactionHistoryProps { - escrow: Escrow; + escrow: IEscrowExtended; } const TransactionHistory: React.FC = ({ escrow }: TransactionHistoryProps) => { // Filter events that represent transactions - const transactionEvents = escrow.events.filter(event => + const transactionEvents = escrow.events.filter((event: any) => event.eventType === 'FUNDED' || event.eventType === 'CONDITION_MET' || event.eventType === 'COMPLETED' || @@ -43,7 +43,7 @@ const TransactionHistory: React.FC = ({ escrow }: Trans - {transactionEvents.map((event) => ( + {transactionEvents.map((event: any) => (
    diff --git a/apps/frontend/hooks/useEscrow.ts b/apps/frontend/hooks/useEscrow.ts index 54d1565..f8a3c13 100644 --- a/apps/frontend/hooks/useEscrow.ts +++ b/apps/frontend/hooks/useEscrow.ts @@ -1,8 +1,8 @@ import { useState, useEffect } from 'react'; -import { Escrow, UseEscrowReturn } from '@/types/escrow'; +import { IEscrowExtended, IUseEscrowReturn } from '@/types/escrow'; -export const useEscrow = (id: string): UseEscrowReturn => { - const [escrow, setEscrow] = useState(null); +export const useEscrow = (id: string): IUseEscrowReturn => { + const [escrow, setEscrow] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); diff --git a/apps/frontend/hooks/useWallet.ts b/apps/frontend/hooks/useWallet.ts index b1f849b..834a133 100644 --- a/apps/frontend/hooks/useWallet.ts +++ b/apps/frontend/hooks/useWallet.ts @@ -1,7 +1,7 @@ import { useState } from 'react'; -import { WalletHookReturn } from '@/types/escrow'; +import { IWalletHookReturn } from '@/types/escrow'; -export const useWallet = (): WalletHookReturn => { +export const useWallet = (): IWalletHookReturn => { const [connected, setConnected] = useState(false); const [publicKey, setPublicKey] = useState(null); diff --git a/apps/frontend/types/escrow.d.ts b/apps/frontend/types/escrow.d.ts deleted file mode 100644 index 50cfa42..0000000 --- a/apps/frontend/types/escrow.d.ts +++ /dev/null @@ -1,58 +0,0 @@ -// Define types based on backend entities -export interface Party { - id: string; - userId: string; - role: 'BUYER' | 'SELLER' | 'ARBITRATOR'; - status: 'PENDING' | 'ACCEPTED' | 'REJECTED'; - createdAt: string; -} - -export interface Condition { - id: string; - description: string; - type: string; - metadata?: Record; -} - -export interface EscrowEvent { - id: string; - eventType: 'CREATED' | 'PARTY_ADDED' | 'PARTY_ACCEPTED' | 'PARTY_REJECTED' | 'FUNDED' | 'CONDITION_MET' | 'STATUS_CHANGED' | 'UPDATED' | 'CANCELLED' | 'COMPLETED' | 'DISPUTED'; - actorId?: string; - data?: Record; - ipAddress?: string; - createdAt: string; -} - -export interface Escrow { - id: string; - title: string; - description?: string; - amount: number; - asset: string; - status: 'PENDING' | 'ACTIVE' | 'COMPLETED' | 'CANCELLED' | 'DISPUTED'; - type: 'STANDARD' | 'MILESTONE' | 'TIMED'; - creatorId: string; - expiresAt?: string; - isActive: boolean; - creator: { - id: string; - walletAddress?: string; - }; - parties: Party[]; - conditions: Condition[]; - events: EscrowEvent[]; - createdAt: string; - updatedAt: string; -} - -export interface UseEscrowReturn { - escrow: Escrow | null; - loading: boolean; - error: string | null; -} - -export interface WalletHookReturn { - connected: boolean; - publicKey: string | null; - connect: () => void; -} \ No newline at end of file diff --git a/apps/frontend/types/escrow.ts b/apps/frontend/types/escrow.ts index b42ab21..d324ebe 100644 --- a/apps/frontend/types/escrow.ts +++ b/apps/frontend/types/escrow.ts @@ -7,7 +7,7 @@ export interface IEscrow { creatorAddress: string; counterpartyAddress: string; deadline: string; // ISO date string - status: 'created' | 'funded' | 'confirmed' | 'released' | 'completed' | 'cancelled' | 'disputed'; + status: 'created' | 'funded' | 'confirmed' | 'released' | 'completed' | 'cancelled' | 'disputed' | 'PENDING' | 'ACTIVE' | 'COMPLETED' | 'CANCELLED' | 'DISPUTED'; createdAt: string; // ISO date string updatedAt: string; // ISO date string milestones?: Array<{ @@ -18,6 +18,57 @@ export interface IEscrow { }>; } +export interface IParty { + id: string; + userId: string; + role: 'BUYER' | 'SELLER' | 'ARBITRATOR'; + status: 'PENDING' | 'ACCEPTED' | 'REJECTED'; + createdAt: string; +} + +export interface ICondition { + id: string; + description: string; + type: string; + metadata?: Record; +} + +export interface IEscrowEvent { + id: string; + eventType: 'CREATED' | 'PARTY_ADDED' | 'PARTY_ACCEPTED' | 'PARTY_REJECTED' | 'FUNDED' | 'CONDITION_MET' | 'STATUS_CHANGED' | 'UPDATED' | 'CANCELLED' | 'COMPLETED' | 'DISPUTED'; + actorId?: string; + data?: Record; + ipAddress?: string; + createdAt: string; +} + +// Extended IEscrow interface to match backend entities +export interface IEscrowExtended extends IEscrow { + type: 'STANDARD' | 'MILESTONE' | 'TIMED'; + creatorId: string; + expiresAt?: string; + isActive: boolean; + creator: { + id: string; + walletAddress?: string; + }; + parties: IParty[]; + conditions: ICondition[]; + events: IEscrowEvent[]; +} + +export interface IUseEscrowReturn { + escrow: IEscrowExtended | null; + loading: boolean; + error: string | null; +} + +export interface IWalletHookReturn { + connected: boolean; + publicKey: string | null; + connect: () => void; +} + export interface IEscrowResponse { escrows: IEscrow[]; hasNextPage: boolean;