Skip to content

Commit

Permalink
added contracts_interaction, AgreementTextArea.tsx and TransactionVie…
Browse files Browse the repository at this point in the history
…w.tsx
  • Loading branch information
jvc-byte committed Oct 9, 2024
1 parent 86a792d commit 9dd7748
Show file tree
Hide file tree
Showing 14 changed files with 400 additions and 85 deletions.
11 changes: 0 additions & 11 deletions .env.local.default

This file was deleted.

11 changes: 0 additions & 11 deletions .env.local.example

This file was deleted.

11 changes: 0 additions & 11 deletions .env.test

This file was deleted.

Binary file removed bun.lockb
Binary file not shown.
8 changes: 6 additions & 2 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@
import Footer from 'src/components/Footer';
// import TransactionWrapper from 'src/components/TransactionWrapper';
// import WalletWrapper from 'src/components/WalletWrapper';
// import { useAccount } from 'wagmi';
import { useAccount } from 'wagmi';
import Header from 'src/components/Header';
import Home from 'src/components/Home';
import { TransactionsView } from 'src/components/TransactionsView';
import ProjectDemo from 'src/components/ProjectDemo';

export default function Page() {
const { isConnected } = useAccount();

return (
<div className="">
<Header />
<div>
<Home />
{isConnected ? <TransactionsView /> : <Home />}
{isConnected && < ProjectDemo/>}
</div>
<Footer />
</div>
Expand Down
34 changes: 34 additions & 0 deletions src/components/AgreementTextArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { useState } from 'react';

const AgreementTextarea = () => {
const [agreement, setAgreement] = useState('');
const wordLimit = 2000;

const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const value = e.target.value;
const words = value.trim().split(/\s+/); // Split by whitespace to get words

if (words.length <= wordLimit) {
setAgreement(value);
} else {
alert(`Word limit of ${wordLimit} exceeded!`);
}
};

return (
<div>
<textarea
className="textarea w-full mb-2 textarea-bordered"
placeholder="Describe The Agreement"
name="description"
value={agreement}
onChange={handleChange}
/>
<div className="text-right text-sm text-gray-600">
{agreement.trim().split(/\s+/).length} / {wordLimit} words
</div>
</div>
);
};

export default AgreementTextarea;
67 changes: 35 additions & 32 deletions src/components/ProjectDemo.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,43 @@

export default function ProjectDemo() {
return (
<>
<div className="hero bg-base-100 min-h-screen">
<div className="hero-content flex-col lg:flex-row-reverse">
<div className="bg-base-100 flex flex-col lg:flex-row items-center lg:items-start px-4 lg:px-8 py-8 lg:py-16">
<div className="w-full lg:w-1/2">
<h2 className='text-base lg:text-xl font-thin landing-pg-intro-txt leading-loose mb-5 animate-slideDown'>Project Demo</h2>
<p className="text-lg lg:text-xl animate-slideUp text-justify mb-5">
A decentralized escrow platform where parties can engage in agreements with full assurance that the terms of the deal will be met before any funds are released. Using smart contracts deployed on the Base blockchain, a secure environment for deal-making is assured.
</p>

<div>
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/HoOt8NeLbv0?si=Iqey3wANeQ-5WGfU"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" >
</iframe>
</div>
<div>
<h2 className='text-base lg:text-xl font-thin landing-pg-intro-txt leading-loose mb-5 animate-slideDown'>Project Demo</h2>
<p className="text-xl animate-slideUp text-justify">A decentralized escrow platform where parties can engage in
agreements with full assurance that the terms of the deal will be met before any funds are released. Using smart
contracts deployed on the Base blockchain, a secure environment for deal-making is assured.
</p>
<div>
<h1 className=" text-lg text-teal-300 mt-5">Key Features of SealedTrust:</h1>
<ul className="text-sm indent-8 ">
<li className="text-sm animate-slideUp text-teal-300 text-justify break-words leading-8">✓ Tamper-proof</li>
<li className="text-sm animate-slideUp text-teal-300 text-justify break-words leading-8">✓ Trustless Escrow</li>
<li className="text-sm animate-slideUp text-teal-300 text-justify break-words leading-8">✓ Fraud Protection</li>
<li className="text-sm animate-slideUp text-teal-300 text-justify break-words leading-8">✓ Transparent Process</li>
<li className="text-sm animate-slideUp text-teal-300 text-justify break-words leading-8">✓ User-Friendly Interface</li>
<li className="text-sm animate-slideUp text-teal-300 text-justify break-words leading-8">✓ Global Accessibility</li>
<li className="text-sm animate-slideUp text-teal-300 text-justify break-words leading-8">⨀ Dispute Resolution via Multisig Wallet</li>
</ul>
</div>
<p className="py-6"></p>
<button className="btn btn-outline btn-accent w-96 glass">Secure Your transaction now!</button>
</div>
</div>
<h1 className="text-lg text-teal-300 mt-5">Key Features of SealedTrust:</h1>
<ul className="text-sm lg:text-base indent-8 space-y-3">
<li className="animate-slideUp text-teal-300 text-justify">✓ Tamper-proof</li>
<li className="animate-slideUp text-teal-300 text-justify">✓ Trustless Escrow</li>
<li className="animate-slideUp text-teal-300 text-justify">✓ Fraud Protection</li>
<li className="animate-slideUp text-teal-300 text-justify">✓ Transparent Process</li>
<li className="animate-slideUp text-teal-300 text-justify">✓ User-Friendly Interface</li>
<li className="animate-slideUp text-teal-300 text-justify">✓ Global Accessibility</li>
<li className="animate-slideUp text-teal-300 text-justify">⨀ Dispute Resolution via Multisig Wallet</li>
</ul>
</div>

</>
<div className="relative w-full lg:w-1/2 mt-8 lg:mt-0 lg:ml-10">
<div className="relative w-full" style={{ paddingTop: '56.25%' }}> {/* 16:9 Aspect Ratio */}
<iframe
className="absolute top-0 left-0 w-full h-full"
src="https://www.youtube-nocookie.com/embed/HoOt8NeLbv0?si=Iqey3wANeQ-5WGfU"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
frameBorder="0"
allowFullScreen
></iframe>
</div>

<div className="mt-8">
<button className="btn btn-outline btn-accent w-full lg:w-96 glass">
Secure Your Transaction Now!
</button>
</div>
</div>
</div>
);
}
2 changes: 1 addition & 1 deletion src/components/SignupButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import WalletWrapper from './WalletWrapper';
export default function SignupButton() {
return (
<WalletWrapper
className="min-w-[90px] btn btn-accent"
className="min-w-[2rem] btn btn-accent"
text="Connect Coinbase"
/>
);
Expand Down
29 changes: 29 additions & 0 deletions src/components/TransactionsView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { CreateAgreement } from './contracts_interaction/WriteContract/CreateAgreement';
import { GetAgreement } from './contracts_interaction/ReadContract/GetAgreement';
import { ConfirmDelivery } from './contracts_interaction/WriteContract/ConfirmDelivery';


export function TransactionsView() {

return (
<div className='flex-cloumn lg:flex mt-20 mb-1'>

{/* Create Aggreement */}
<div className="mt-4 w-1/2 bg-base-100 p-6 h-1/2 container lg:mr-1 lg:ml-1 rounded-lg">
<h1 className="block text-justify lg:text-center pl-2 text-base lg:text-3xl text-teal-200 tracking-widest font-extrabold bg-opacity-80 bg-base-100 mb-4">Set Agreement</h1>

<CreateAgreement />

</div>

{/* View Agreement State */}
<div className="mt-4 w-1/2 bg-base-100 p-6 h-1/2 container lg:mr-1 lg:ml-1 rounded-lg">
<h1 className="block text-justify lg:text-center pl-2 text-base lg:text-3xl text-teal-200 tracking-widest font-extrabold bg-opacity-80 bg-base-100 mb-4">Track Agreement State</h1>

<GetAgreement />
<ConfirmDelivery />

</div>
</div >
)
}
149 changes: 149 additions & 0 deletions src/components/contracts_interaction/ReadContract/GetAgreement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import React, { FormEvent, useState, useCallback } from 'react';
import { type Hex, formatEther, BaseError } from 'viem';
import { useReadContract } from 'wagmi';

// Assuming you have your contract ABI and address defined somewhere
import { contractAbi, contractAddress } from '../../../constants';

// Define the State enum to match your contract
enum State {
"AWAITING PAYMENT",
"AWAITING DELIVERY",
COMPLETE,
REFUNDED
}

interface AgreementDetails {
buyer: Hex;
seller: Hex;
amount: bigint;
state: State;
description: string;
}

const shortenAddress = (address: string) => {
return `${address.slice(0, 6)}...${address.slice(-4)}`;
};

interface CopyIconProps {
isCopied: boolean;
}

const CopyIcon: React.FC<CopyIconProps> = ({ isCopied }) => (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={`transition-all duration-300 ${isCopied ? 'text-green-500' : 'text-gray-500'}`}>
{isCopied ? (
<path d="M20 6L9 17l-5-5" />
) : (
<>
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</>
)}
</svg>
);

export function GetAgreement() {
const [agreementId, setAgreementId] = useState<string>('');
const [agreementDetails, setAgreementDetails] = useState<AgreementDetails | null>(null);
const [copiedStates, setCopiedStates] = useState({
buyer: false,
seller: false
});

const { data, error, isLoading, refetch } = useReadContract({
address: contractAddress,
abi: contractAbi,
functionName: 'getAgreement',
args: [BigInt(agreementId || '0')],
});

const fetchAgreementDetails = useCallback(async () => {
if (agreementId) {
const result = await refetch();
if (result.data) {
setAgreementDetails({
buyer: result.data[0],
seller: result.data[1],
amount: result.data[2],
state: result.data[3],
description: result.data[4],
});
}
}
}, [agreementId, refetch]);

async function submit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const id = formData.get('agreementId') as string;
setAgreementId(id);
await fetchAgreementDetails();
}

const copyToClipboard = (text: string, field: 'buyer' | 'seller') => {
navigator.clipboard.writeText(text).then(() => {
setCopiedStates(prev => ({ ...prev, [field]: true }));
setTimeout(() => {
setCopiedStates(prev => ({ ...prev, [field]: false }));
}, 2000); // Reset after 2 seconds
}).catch(err => {
console.error('Failed to copy: ', err);
});
};

const formatAmount = (amount: bigint) => {
const ethAmount = parseFloat(formatEther(amount));
return `${ethAmount.toFixed(4)} ETH`;
};

return (
<div className="pb-7">
<form className="set" onSubmit={submit}>
<h1 className="pl-2 text-base lg:text-xl text-teal-200 tracking-widest font-extrabold bg-opacity-80 bg-base-100 mb-4">Check Agreement Status</h1>
<label className="input input-bordered flex items-center gap-2 mb-4">
<input
className="grow"
name="agreementId"
placeholder="Agreement ID"
type="number"
step="1"
required
value={agreementId}
onChange={(e) => setAgreementId(e.target.value)}
/>
</label>
<button disabled={isLoading} type="submit" className='btn btn-outline btn-accent w-full text-base'>
{isLoading ? 'Fetching...' : 'Get Agreement Details'}
</button>
</form>
{isLoading && <div>Fetching agreement details...</div>}
{error && (<div>Error: {(error as BaseError).shortMessage || error.message}</div>)}
{agreementDetails && (

<div className="mt-4">
<div tabIndex={0} className="collapse collapse-arrow border-base-300 bg-base-200 border">
<div className="collapse-title text-lg font-bold">View Agreement Details:</div>
<div className="collapse-content">
<p className="flex items-center">
Buyer: {shortenAddress(agreementDetails.buyer)}
<button onClick={() => copyToClipboard(agreementDetails.buyer, 'buyer')} className="ml-2 focus:outline-none">
<CopyIcon isCopied={copiedStates.buyer} />
</button>
</p>
<p className="flex items-center">
Seller: {shortenAddress(agreementDetails.seller)}
<button onClick={() => copyToClipboard(agreementDetails.seller, 'seller')} className="ml-2 focus:outline-none">
<CopyIcon isCopied={copiedStates.seller} />
</button>
</p>
<p>Amount: {formatAmount(agreementDetails.amount)}</p>
<p>State: {State[agreementDetails.state]}</p>
<p>Description: {agreementDetails.description}</p>
</div>
</div>
</div>

)}
</div>
);
}
Loading

0 comments on commit 9dd7748

Please sign in to comment.