From 2b6dc172077acb7db55cddfc5f6dfc3625abe6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Brenes?= Date: Wed, 25 Jun 2025 17:57:16 -0600 Subject: [PATCH 1/6] feat: new base project --- .env.example | 10 +- README.md | 130 +---- package-lock.json | 279 ++++++++--- package.json | 3 +- src/@types/pool.entity.ts | 25 + src/app/about-us/page.tsx | 17 - src/app/dashboard/chat/[wallet]/page.tsx | 6 - src/app/dashboard/chat/page.tsx | 6 - src/app/dashboard/layout.tsx | 15 +- src/app/dashboard/loans/page.tsx | 5 - src/app/dashboard/marketplace/page.tsx | 5 - src/app/dashboard/pools/[id]/page.tsx | 5 + src/app/dashboard/pools/page.tsx | 5 + src/app/dashboard/profile/page.tsx | 2 +- src/app/dashboard/stats/page.tsx | 151 ++++++ src/app/maintenance/page.tsx | 23 - src/app/page.tsx | 5 +- src/components/layouts/header/Header.tsx | 145 ++++-- src/components/layouts/header/HeaderHome.tsx | 75 ++- .../layouts/sidebar/hooks/useSidebar.hook.tsx | 13 +- src/components/middleware.ts | 26 - .../modules/about-us/hooks/useAbout.hook.ts | 66 --- .../modules/about-us/ui/pages/AboutUs.tsx | 81 --- src/components/modules/auth/ui/pages/Home.tsx | 130 ++--- .../modules/blend/ui/cards/PoolCard.tsx | 131 +++++ .../modules/blend/ui/detail/PoolDetail.tsx | 252 ++++++++++ .../modules/blend/ui/pages/PoolDetailPage.tsx | 10 + .../modules/blend/ui/pages/PoolsPage.tsx | 38 ++ .../modules/chat/hooks/chat.hook.ts | 31 -- .../modules/chat/hooks/use-all-chats.hook.ts | 74 --- .../modules/chat/hooks/wallet-chat.hook.ts | 74 --- src/components/modules/chat/lib/chat.ts | 280 ----------- .../modules/chat/ui/components/chat-list.tsx | 130 ----- .../chat/ui/components/message-bubble.tsx | 70 --- .../modules/chat/ui/dialogs/ChatDialog.tsx | 325 ------------ .../dashboard/hooks/useDashboard.hook.ts | 12 +- .../dashboard/ui/pages/DashboardPage.tsx | 467 +++++++----------- .../pages/background/GradientBackground.tsx | 50 -- .../constants/initialize-steps.constant.ts | 22 - .../escrows/constants/trustline.constant.ts | 19 - .../hooks/change-milestone-flag-form.hook.ts | 133 ----- .../change-milestone-status-form.hook.ts | 137 ----- .../escrows/hooks/fund-escrow-form.hook.ts | 97 ---- .../escrows/hooks/get-escrow-form.hook.ts | 77 --- .../get-multiple-escrow-balances-form.hook.ts | 91 ---- .../hooks/initialize-escrow-form.hook.ts | 269 ---------- .../escrows/hooks/release-funds-form.hook.ts | 121 ----- .../hooks/resolve-dispute-form.hook.ts | 126 ----- .../escrows/hooks/start-dispute-form.hook.ts | 119 ----- .../escrows/hooks/update-escrow-form.hook.ts | 153 ------ .../change-milestone-flag-form.schema.ts | 16 - .../change-milestone-status-form.schema.ts | 17 - .../schemas/fund-escrow-form.schema.ts | 9 - .../escrows/schemas/get-escrow-form.schema.ts | 6 - ...et-multiple-escrow-balances-form.schema.ts | 12 - .../schemas/initialize-escrow-form.schema.ts | 94 ---- .../schemas/release-funds-form.schema.ts | 15 - .../schemas/resolve-dispute-form.schema.ts | 16 - .../schemas/start-dispute-form.schema.ts | 6 - .../schemas/update-escrow-form.schema.ts | 99 ---- .../escrows/ui/ConnectWalletWarning.tsx | 30 -- .../modules/escrows/ui/cards/EntityCard.tsx | 20 - .../escrows/ui/endpoints/DeployEndpoints.tsx | 71 --- .../escrows/ui/endpoints/EscrowEndpoints.tsx | 123 ----- .../escrows/ui/endpoints/HelperEndpoints.tsx | 44 -- .../ui/forms/ChangeMilestoneFlagForm.tsx | 127 ----- .../ui/forms/ChangeMilestoneStatusForm.tsx | 137 ----- .../escrows/ui/forms/FundEscrowForm.tsx | 85 ---- .../escrows/ui/forms/GetEscrowForm.tsx | 58 --- .../ui/forms/GetMultipleEscrowBalanceForm.tsx | 95 ---- .../escrows/ui/forms/InitializeEscrowForm.tsx | 448 ----------------- .../escrows/ui/forms/ReleaseFundsForm.tsx | 80 --- .../escrows/ui/forms/ResolveDisputeForm.tsx | 95 ---- .../escrows/ui/forms/StartDisputeForm.tsx | 66 --- .../escrows/ui/forms/UpdateEscrowForm.tsx | 362 -------------- .../modules/escrows/ui/pages/dashboard.tsx | 32 -- .../ui/sections/EscrowCreatedSection.tsx | 116 ----- .../ui/sections/EscrowDetailsSection.tsx | 39 -- .../ui/sections/EscrowMilestonesSection.tsx | 86 ---- .../ui/sections/FinancialDetailsSection.tsx | 49 -- .../escrows/ui/sections/HeaderSection.tsx | 81 --- .../modules/escrows/ui/tabs/MainTabs.tsx | 40 -- .../modules/maintenance/hooks/useCountdown.ts | 46 -- .../modules/maintenance/ui/CountdownTimer.tsx | 41 -- .../marketplace/hooks/marketplace.hook.ts | 161 ------ .../marketplace/ui/pages/MarketplacePage.tsx | 278 ----------- .../modules/profile/ui/UserProfileForm.tsx | 402 +++++++-------- src/components/ui/accordion.tsx | 58 +++ src/components/ui/alert.tsx | 66 +++ .../build-escrow-from-response.helper.ts | 47 -- src/lib/blend/config.ts | 3 + src/lib/blend/helpers.ts | 30 ++ src/providers/escrow.provider.tsx | 59 --- src/providers/global.provider.tsx | 12 +- src/providers/tabs.provider.tsx | 2 +- src/providers/trustless-work.provider.tsx | 30 -- src/utils/errors/escrow-errors.ts | 37 -- src/utils/response-display.tsx | 88 ---- 98 files changed, 1647 insertions(+), 6823 deletions(-) create mode 100644 src/@types/pool.entity.ts delete mode 100644 src/app/about-us/page.tsx delete mode 100644 src/app/dashboard/chat/[wallet]/page.tsx delete mode 100644 src/app/dashboard/chat/page.tsx delete mode 100644 src/app/dashboard/loans/page.tsx delete mode 100644 src/app/dashboard/marketplace/page.tsx create mode 100644 src/app/dashboard/pools/[id]/page.tsx create mode 100644 src/app/dashboard/pools/page.tsx create mode 100644 src/app/dashboard/stats/page.tsx delete mode 100644 src/app/maintenance/page.tsx delete mode 100644 src/components/middleware.ts delete mode 100644 src/components/modules/about-us/hooks/useAbout.hook.ts delete mode 100644 src/components/modules/about-us/ui/pages/AboutUs.tsx create mode 100644 src/components/modules/blend/ui/cards/PoolCard.tsx create mode 100644 src/components/modules/blend/ui/detail/PoolDetail.tsx create mode 100644 src/components/modules/blend/ui/pages/PoolDetailPage.tsx create mode 100644 src/components/modules/blend/ui/pages/PoolsPage.tsx delete mode 100644 src/components/modules/chat/hooks/chat.hook.ts delete mode 100644 src/components/modules/chat/hooks/use-all-chats.hook.ts delete mode 100644 src/components/modules/chat/hooks/wallet-chat.hook.ts delete mode 100644 src/components/modules/chat/lib/chat.ts delete mode 100644 src/components/modules/chat/ui/components/chat-list.tsx delete mode 100644 src/components/modules/chat/ui/components/message-bubble.tsx delete mode 100644 src/components/modules/chat/ui/dialogs/ChatDialog.tsx delete mode 100644 src/components/modules/dashboard/ui/pages/background/GradientBackground.tsx delete mode 100644 src/components/modules/escrows/constants/initialize-steps.constant.ts delete mode 100644 src/components/modules/escrows/constants/trustline.constant.ts delete mode 100644 src/components/modules/escrows/hooks/change-milestone-flag-form.hook.ts delete mode 100644 src/components/modules/escrows/hooks/change-milestone-status-form.hook.ts delete mode 100644 src/components/modules/escrows/hooks/fund-escrow-form.hook.ts delete mode 100644 src/components/modules/escrows/hooks/get-escrow-form.hook.ts delete mode 100644 src/components/modules/escrows/hooks/get-multiple-escrow-balances-form.hook.ts delete mode 100644 src/components/modules/escrows/hooks/initialize-escrow-form.hook.ts delete mode 100644 src/components/modules/escrows/hooks/release-funds-form.hook.ts delete mode 100644 src/components/modules/escrows/hooks/resolve-dispute-form.hook.ts delete mode 100644 src/components/modules/escrows/hooks/start-dispute-form.hook.ts delete mode 100644 src/components/modules/escrows/hooks/update-escrow-form.hook.ts delete mode 100644 src/components/modules/escrows/schemas/change-milestone-flag-form.schema.ts delete mode 100644 src/components/modules/escrows/schemas/change-milestone-status-form.schema.ts delete mode 100644 src/components/modules/escrows/schemas/fund-escrow-form.schema.ts delete mode 100644 src/components/modules/escrows/schemas/get-escrow-form.schema.ts delete mode 100644 src/components/modules/escrows/schemas/get-multiple-escrow-balances-form.schema.ts delete mode 100644 src/components/modules/escrows/schemas/initialize-escrow-form.schema.ts delete mode 100644 src/components/modules/escrows/schemas/release-funds-form.schema.ts delete mode 100644 src/components/modules/escrows/schemas/resolve-dispute-form.schema.ts delete mode 100644 src/components/modules/escrows/schemas/start-dispute-form.schema.ts delete mode 100644 src/components/modules/escrows/schemas/update-escrow-form.schema.ts delete mode 100644 src/components/modules/escrows/ui/ConnectWalletWarning.tsx delete mode 100644 src/components/modules/escrows/ui/cards/EntityCard.tsx delete mode 100644 src/components/modules/escrows/ui/endpoints/DeployEndpoints.tsx delete mode 100644 src/components/modules/escrows/ui/endpoints/EscrowEndpoints.tsx delete mode 100644 src/components/modules/escrows/ui/endpoints/HelperEndpoints.tsx delete mode 100644 src/components/modules/escrows/ui/forms/ChangeMilestoneFlagForm.tsx delete mode 100644 src/components/modules/escrows/ui/forms/ChangeMilestoneStatusForm.tsx delete mode 100644 src/components/modules/escrows/ui/forms/FundEscrowForm.tsx delete mode 100644 src/components/modules/escrows/ui/forms/GetEscrowForm.tsx delete mode 100644 src/components/modules/escrows/ui/forms/GetMultipleEscrowBalanceForm.tsx delete mode 100644 src/components/modules/escrows/ui/forms/InitializeEscrowForm.tsx delete mode 100644 src/components/modules/escrows/ui/forms/ReleaseFundsForm.tsx delete mode 100644 src/components/modules/escrows/ui/forms/ResolveDisputeForm.tsx delete mode 100644 src/components/modules/escrows/ui/forms/StartDisputeForm.tsx delete mode 100644 src/components/modules/escrows/ui/forms/UpdateEscrowForm.tsx delete mode 100644 src/components/modules/escrows/ui/pages/dashboard.tsx delete mode 100644 src/components/modules/escrows/ui/sections/EscrowCreatedSection.tsx delete mode 100644 src/components/modules/escrows/ui/sections/EscrowDetailsSection.tsx delete mode 100644 src/components/modules/escrows/ui/sections/EscrowMilestonesSection.tsx delete mode 100644 src/components/modules/escrows/ui/sections/FinancialDetailsSection.tsx delete mode 100644 src/components/modules/escrows/ui/sections/HeaderSection.tsx delete mode 100644 src/components/modules/escrows/ui/tabs/MainTabs.tsx delete mode 100644 src/components/modules/maintenance/hooks/useCountdown.ts delete mode 100644 src/components/modules/maintenance/ui/CountdownTimer.tsx delete mode 100644 src/components/modules/marketplace/hooks/marketplace.hook.ts delete mode 100644 src/components/modules/marketplace/ui/pages/MarketplacePage.tsx create mode 100644 src/components/ui/accordion.tsx create mode 100644 src/components/ui/alert.tsx delete mode 100644 src/helpers/build-escrow-from-response.helper.ts create mode 100644 src/lib/blend/config.ts create mode 100644 src/lib/blend/helpers.ts delete mode 100644 src/providers/escrow.provider.tsx delete mode 100644 src/providers/trustless-work.provider.tsx delete mode 100644 src/utils/errors/escrow-errors.ts delete mode 100644 src/utils/response-display.tsx diff --git a/.env.example b/.env.example index fa30dbd7..39a28e31 100644 --- a/.env.example +++ b/.env.example @@ -4,12 +4,4 @@ NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN= NEXT_PUBLIC_FIREBASE_PROJECT_ID= NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET= NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID= -NEXT_PUBLIC_FIREBASE_APP_ID= - -# TRUSTLESS WORK -NEXT_PUBLIC_API_KEY= - -# MAINTENANCE MODE -NEXT_PUBLIC_MAINTENANCE_MODE=false -NEXT_PUBLIC_COUNTDOWN_HOURS=2 -NEXT_PUBLIC_COUNTDOWN_MINUTES=30 \ No newline at end of file +NEXT_PUBLIC_FIREBASE_APP_ID= \ No newline at end of file diff --git a/README.md b/README.md index a2c5db91..efd03a99 100644 --- a/README.md +++ b/README.md @@ -1,129 +1,43 @@ -![ ](https://github.com/user-attachments/assets/9201806d-7116-44d7-9df0-6f73c6f3d3f3) - # TrustBridge -**TrustBridge** is a decentralized lending platform built on the Stellar blockchain and integrated with Trustless Work for smart contract management. It enables users to request and fund secure loans, ensuring transparency, automation, and security without traditional intermediaries. - -## 🎯 Key Benefits and Objectives - -- Facilitate access to secure, decentralized loans via the Stellar network. -- Connect borrowers and lenders through a transparent marketplace. -- Automate escrow creation with Trustless Work for secure fund handling. - -## 🔒 Why Choose TrustBridge? - -### ⚙️ Core Advantages - -- **Security:** Funds are managed by smart contracts, not centralized entities. -- **Transparency:** All loan activities are verifiable on the blockchain. -- **Efficiency:** Fast transactions and no intermediaries. -- **Smart Automation:** Conditions are enforced automatically via escrow contracts. - -## 🌟 Marketplace Features - -- 🧾 Browse approved loan requests -- 📊 View key loan details: amount, borrower, date, conditions -- 🔎 Search and filter by title, minimum amount, and date -- 🧩 Fund directly from wallet via Trustless Work API -- 🔐 Fully decentralized and trustless escrow deployment - -## 🛠️ How It Works +Integration prototype between **TrustBridge** and **Blend.Capital** enabling **cross-chain lending** on the Stellar blockchain using bridged assets and Blend’s permissionless lending infrastructure. -1. **Borrower submits a loan request.** -2. **Loan is reviewed and approved by the platform.** -3. **The request appears in the marketplace.** -4. **Lenders fund loans via their Stellar wallet.** -5. **A smart contract is deployed through Trustless Work.** -6. **Loan is disbursed automatically.** +🔗 Built on Stellar + Soroban +🧪 MVP running on testnet +💱 Use case: Interoperable Lending with bridged assets (e.g., USDC, BLND) ---- +## Getting Started -## ⚙️ Getting Started - -Follow the steps below to get started with this project: - -### 📦 Installation - -1. Install dependencies: +1. Install dependencies, including the Blend SDK: ```bash -npm i +npm install ``` -2. Format the code using Prettier: +2. Copy the `.env.example` file to `.env` and configure the following variables: -```bash -npx prettier --write . -``` + - Blend contract addresses + - Wallet address + - RPC or Soroban network details (testnet or mainnet) -3. Start the development server: +3. Launch the development server: ```bash npm run dev ``` ---- - -## 🔐 Environment Variables - -Create a `.env` file in the root of the project with the following: - -```env -# Firebase configuration -NEXT_PUBLIC_FIREBASE_API_KEY=TU_API_KEY -NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=TU_AUTH_DOMAIN -NEXT_PUBLIC_FIREBASE_PROJECT_ID=TU_PROJECT_ID -NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=TU_STORAGE_BUCKET -NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=TU_MESSAGING_SENDER_ID -NEXT_PUBLIC_FIREBASE_APP_ID=TU_APP_ID - -# Trustless Work API -NEXT_PUBLIC_API_KEY=TU_API_KEY -``` - -### API Key Video - -[dApp Trustless Work](https://dapp.trustlesswork.com) - -https://github.com/user-attachments/assets/69f0adf2-cb5f-48ff-a4eb-bb1870fa35fa - -### Firebase Video - -[Firebase](https://firebase.google.com) - -https://github.com/user-attachments/assets/0c4a8a80-33f1-41ae-819b-6a38abf30e4b - ---- - -## 🔑 Wallet Requirements - -To use this platform, install one of the following wallets: - -- Freighter -- Albedo -- xBull -- LOBSTR - -### Wallet Usage Note - -Ensure your wallet is set to "test net". If you see "Not Available" in Freighter: - -- Go to **Security > Manage Connected Wallets** -- Remove **localhost** -- Reload and reconnect - -If problems persist, contact support. - ---- - -## 🧠 IMPORTANT NOTE (Husky Setup) +## Architecture Overview -We use **Husky** to ensure code formatting and linting on `git push`. If `npm run format` or `npm run lint` fail, your push will be blocked. +- **TrustBridge** handles asset bridging and cross-chain messaging. +- **Blend** manages lending, borrowing, and liquidations in a permissionless environment. +- Integration is implemented using Soroban smart contracts and TypeScript interfaces. -Fix any errors before retrying your push. +## Status ---- +- Functional MVP live on Stellar testnet +- Supports USDC and BLND lending pools +- Oracle integration via custom `oracle-mock` Soroban contract -## 📜 License +## License -© 2025 TrustBridge. Released under the MIT License. +MIT diff --git a/package-lock.json b/package-lock.json index 1dda209f..25939f1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,10 @@ "name": "project", "version": "0.1.0", "dependencies": { + "@blend-capital/blend-sdk": "^2.2.0", "@creit.tech/stellar-wallets-kit": "^1.7.3", "@hookform/resolvers": "^5.0.0", + "@radix-ui/react-accordion": "^1.2.11", "@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-checkbox": "1.2.3", "@radix-ui/react-collapsible": "^1.1.3", @@ -27,7 +29,6 @@ "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.8", - "@trustless-work/escrow": "0.1.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -228,6 +229,53 @@ "node": ">=6.9.0" } }, + "node_modules/@blend-capital/blend-sdk": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@blend-capital/blend-sdk/-/blend-sdk-2.2.0.tgz", + "integrity": "sha512-S2P7D1Y45IKBk381gvPWt7rv587B2FUY9Vd8KyXpxkpcB8/IgFeCItoVBidqIW5vbsAG3T1fwHJbcI3bUfFlpw==", + "license": "MIT", + "dependencies": { + "@stellar/stellar-sdk": "13.0.0", + "buffer": "6.0.3", + "follow-redirects": ">=1.15.6" + } + }, + "node_modules/@blend-capital/blend-sdk/node_modules/@stellar/stellar-base": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-13.1.0.tgz", + "integrity": "sha512-90EArG+eCCEzDGj3OJNoCtwpWDwxjv+rs/RNPhvg4bulpjN/CSRj+Ys/SalRcfM4/WRC5/qAfjzmJBAuquWhkA==", + "license": "Apache-2.0", + "dependencies": { + "@stellar/js-xdr": "^3.1.2", + "base32.js": "^0.1.0", + "bignumber.js": "^9.1.2", + "buffer": "^6.0.3", + "sha.js": "^2.3.6", + "tweetnacl": "^1.0.3" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "sodium-native": "^4.3.3" + } + }, + "node_modules/@blend-capital/blend-sdk/node_modules/@stellar/stellar-sdk": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-13.0.0.tgz", + "integrity": "sha512-+wvmKi+XWwu27nLYTM17EgBdpbKohEkOfCIK4XKfsI4WpMXAqvnqSm98i9h5dAblNB+w8BJqzGs1JY0PtTGm4A==", + "license": "Apache-2.0", + "dependencies": { + "@stellar/stellar-base": "^13.0.1", + "axios": "^1.7.7", + "bignumber.js": "^9.1.2", + "eventsource": "^2.0.2", + "feaxios": "^0.0.20", + "randombytes": "^2.1.0", + "toml": "^3.0.0", + "urijs": "^1.19.1" + } + }, "node_modules/@creit.tech/stellar-wallets-kit": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@creit.tech/stellar-wallets-kit/-/stellar-wallets-kit-1.7.3.tgz", @@ -3208,6 +3256,104 @@ "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", "license": "MIT" }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.11.tgz", + "integrity": "sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collapsible": "1.1.11", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.4.tgz", @@ -3457,9 +3603,9 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.8.tgz", - "integrity": "sha512-hxEsLvK9WxIAPyxdDRULL4hcaSjMZCfP7fHB0Z1uUnDoDBat1Zh46hwYfa69DeZAbJrPckjf0AGAtEZyvDyJbw==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz", + "integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -3467,7 +3613,7 @@ "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -3486,6 +3632,47 @@ } } }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.4.tgz", @@ -5697,8 +5884,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz", "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@stellar/stellar-base": { "version": "12.1.1", @@ -6017,32 +6203,6 @@ "tailwindcss": "4.1.5" } }, - "node_modules/@tanstack/query-core": { - "version": "5.76.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.76.2.tgz", - "integrity": "sha512-PFGwWh5ss9cJQ67l6bZ7hqXbisX2gy13G2jP+VGY1bgdbCfOMWh6UBVnN62QbFXro6CCoX9hYzTnZHr6Rz00YQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.76.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.76.2.tgz", - "integrity": "sha512-rGkWberCrFdIxMdvSAJM/UOKeu0O/JVTbMmfhQoJpiU9Uq0EDx2EMCadnNuJWbXR4smDA2t7DY3NKkYFmDVS5A==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.76.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, "node_modules/@trezor/analytics": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@trezor/analytics/-/analytics-1.3.3.tgz", @@ -7554,20 +7714,6 @@ "tslib": "^2.6.2" } }, - "node_modules/@trustless-work/escrow": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@trustless-work/escrow/-/escrow-0.1.8.tgz", - "integrity": "sha512-c4awMnn0DrMsJBUUJqV0C23lLHCfvUl870yC91WE8ZVcfBgXKz3ZXoiAK4FTMPDE+tPsBkf1Eu7plP60nhA5tg==", - "license": "MIT", - "dependencies": { - "@tanstack/react-query": "^5.76.1", - "axios": "^1.9.0" - }, - "peerDependencies": { - "react": ">=18.0.0 <20.0.0", - "react-dom": ">=18.0.0 <20.0.0" - } - }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -9082,7 +9228,6 @@ "integrity": "sha512-unn6Vy/Yke6F99vg/7tcrvM2KUvIhTNniaSqDbam4AWkd4NhvDVSrQiRYVlNzUV2P7SPobkCK7JFVxrJk9btCg==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "bare-module-resolve": "^1.10.0", "bare-semver": "^1.0.0" @@ -9102,7 +9247,6 @@ "integrity": "sha512-C9COe/GhWfVXKytW3DElTkiBU+Gb2OXeaVkdGdRB/lp26TVLESHkTGS876iceAGdvtPgohfp9nX8vXHGvN3++Q==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "bare-semver": "^1.0.0" }, @@ -9121,7 +9265,6 @@ "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==", "license": "Apache-2.0", "optional": true, - "peer": true, "engines": { "bare": ">=1.14.0" } @@ -9132,7 +9275,6 @@ "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "bare-os": "^3.0.1" } @@ -9142,8 +9284,7 @@ "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.0.1.tgz", "integrity": "sha512-UtggzHLiTrmFOC/ogQ+Hy7VfoKoIwrP1UFcYtTxoCUdLtsIErT8+SWtOC2DH/snT9h+xDrcBEPcwKei1mzemgg==", "license": "Apache-2.0", - "optional": true, - "peer": true + "optional": true }, "node_modules/bare-url": { "version": "2.1.6", @@ -9151,7 +9292,6 @@ "integrity": "sha512-FgjDeR+/yDH34By4I0qB5NxAoWv7dOTYcOXwn73kr+c93HyC2lU6tnjifqUe33LKMJcDyCYPQjEAqgOQiXkE2Q==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "bare-path": "^3.0.0" } @@ -9170,7 +9310,6 @@ "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.12.0" } @@ -11075,7 +11214,6 @@ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.0.0" } @@ -11221,6 +11359,15 @@ "node": ">=0.8.0" } }, + "node_modules/feaxios": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/feaxios/-/feaxios-0.0.20.tgz", + "integrity": "sha512-g3hm2YDNffNxA3Re3Hd8ahbpmDee9Fv1Pb1C/NoWsjY7mtD8nyNeJytUzn+DK0Hyl9o6HppeWOrtnqgmhOYfWA==", + "license": "MIT", + "dependencies": { + "is-retry-allowed": "^3.0.0" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -12346,6 +12493,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-retry-allowed": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-3.0.0.tgz", + "integrity": "sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-set": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", @@ -14718,7 +14877,6 @@ "integrity": "sha512-KbXAD5q2+v1GJnkzd8zzbOxchTkStSyJZ9QwoCq3QwEXAaIlG3wDYRZGzVD357jmwaGY7hr5VaoEAL0BkF0Kvg==", "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "bare-addon-resolve": "^1.3.0", "bare-url": "^2.1.0" @@ -15496,7 +15654,6 @@ "integrity": "sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "require-addon": "^1.1.0" } @@ -16047,8 +16204,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tr46": { "version": "0.0.3", @@ -16535,8 +16691,7 @@ "version": "1.19.11", "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/usb": { "version": "2.15.0", diff --git a/package.json b/package.json index 5c7b5195..2f1105e0 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,10 @@ "eslint": "eslint --ext .js,.jsx,.ts,.tsx ." }, "dependencies": { + "@blend-capital/blend-sdk": "^2.2.0", "@creit.tech/stellar-wallets-kit": "^1.7.3", "@hookform/resolvers": "^5.0.0", + "@radix-ui/react-accordion": "^1.2.11", "@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-checkbox": "1.2.3", "@radix-ui/react-collapsible": "^1.1.3", @@ -35,7 +37,6 @@ "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.8", - "@trustless-work/escrow": "0.1.8", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/src/@types/pool.entity.ts b/src/@types/pool.entity.ts new file mode 100644 index 00000000..308ed584 --- /dev/null +++ b/src/@types/pool.entity.ts @@ -0,0 +1,25 @@ +export type PoolData = { + name?: string; + assets?: string[]; + interest?: { + base?: number; + }; + depositApy?: number; + borrowApy?: number; + ltv?: number; + liquidationThreshold?: number; + risk?: Record; + backstop?: { + tvl?: number; + withdrawalPct?: number; + rewardZone?: boolean; + }; +}; + +export type UserData = { + bTokens?: number; + dTokens?: number; + collateral?: number; + debt?: number; + healthFactor?: number; +}; diff --git a/src/app/about-us/page.tsx b/src/app/about-us/page.tsx deleted file mode 100644 index acd76f96..00000000 --- a/src/app/about-us/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -"use client"; - -import AboutPage from "@/components/modules/about-us/ui/pages/AboutUs"; -import { GradientBackground } from "@/components/modules/dashboard/ui/pages/background/GradientBackground"; -import { ScrollArea } from "@/components/ui/scroll-area"; - -export default function Page() { - return ( - - -
- -
-
-
- ); -} diff --git a/src/app/dashboard/chat/[wallet]/page.tsx b/src/app/dashboard/chat/[wallet]/page.tsx deleted file mode 100644 index ec3ca592..00000000 --- a/src/app/dashboard/chat/[wallet]/page.tsx +++ /dev/null @@ -1,6 +0,0 @@ -"use client"; -import { ChatDialog } from "@/components/modules/chat/ui/dialogs/ChatDialog"; - -export default function ChatPage() { - return ; -} diff --git a/src/app/dashboard/chat/page.tsx b/src/app/dashboard/chat/page.tsx deleted file mode 100644 index ec3ca592..00000000 --- a/src/app/dashboard/chat/page.tsx +++ /dev/null @@ -1,6 +0,0 @@ -"use client"; -import { ChatDialog } from "@/components/modules/chat/ui/dialogs/ChatDialog"; - -export default function ChatPage() { - return ; -} diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx index 9b50bf97..b3c52d6b 100644 --- a/src/app/dashboard/layout.tsx +++ b/src/app/dashboard/layout.tsx @@ -1,19 +1,14 @@ -import { SidebarProvider } from "@/components/ui/sidebar"; -import { TrustBridgeSidebar } from "@/components/layouts/sidebar/Sidebar"; import { Header } from "@/components/layouts/header/Header"; import { ScrollArea } from "@/components/ui/scroll-area"; const Layout = ({ children }: { children: React.ReactNode }) => { return ( - -
- -
-
- {children} -
+
+
+
+ {children}
- +
); }; diff --git a/src/app/dashboard/loans/page.tsx b/src/app/dashboard/loans/page.tsx deleted file mode 100644 index 106f9847..00000000 --- a/src/app/dashboard/loans/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Loans } from "@/components/modules/escrows/ui/pages/dashboard"; - -export default function Page() { - return ; -} diff --git a/src/app/dashboard/marketplace/page.tsx b/src/app/dashboard/marketplace/page.tsx deleted file mode 100644 index b24f210c..00000000 --- a/src/app/dashboard/marketplace/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { MarketplacePage } from "@/components/modules/marketplace/ui/pages/MarketplacePage"; - -export default function Page() { - return ; -} diff --git a/src/app/dashboard/pools/[id]/page.tsx b/src/app/dashboard/pools/[id]/page.tsx new file mode 100644 index 00000000..d675c97a --- /dev/null +++ b/src/app/dashboard/pools/[id]/page.tsx @@ -0,0 +1,5 @@ +import { PoolDetailPage } from "@/components/modules/blend/ui/pages/PoolDetailPage"; + +export default function Page({ params }: { params: { id: string } }) { + return ; +} diff --git a/src/app/dashboard/pools/page.tsx b/src/app/dashboard/pools/page.tsx new file mode 100644 index 00000000..0250c435 --- /dev/null +++ b/src/app/dashboard/pools/page.tsx @@ -0,0 +1,5 @@ +import { PoolsPage } from "@/components/modules/blend/ui/pages/PoolsPage"; + +export default function Page() { + return ; +} diff --git a/src/app/dashboard/profile/page.tsx b/src/app/dashboard/profile/page.tsx index af499e34..4e977d3d 100644 --- a/src/app/dashboard/profile/page.tsx +++ b/src/app/dashboard/profile/page.tsx @@ -1,8 +1,8 @@ "use client"; -import { UserProfileForm } from "@/components/modules/profile/ui/UserProfileForm"; import { useWalletContext } from "@/providers/wallet.provider"; import { Card } from "@/components/ui/card"; +import UserProfileForm from "@/components/modules/profile/ui/UserProfileForm"; export default function SettingsPage() { const { walletAddress } = useWalletContext(); diff --git a/src/app/dashboard/stats/page.tsx b/src/app/dashboard/stats/page.tsx new file mode 100644 index 00000000..c2cc107c --- /dev/null +++ b/src/app/dashboard/stats/page.tsx @@ -0,0 +1,151 @@ +"use client"; +import { useEffect, useState } from "react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { fetchStats } from "@/lib/blend/helpers"; +import { DollarSign, ListChecks, BarChartBig, RefreshCw } from "lucide-react"; + +interface PlatformStats { + totalTvl: number; + activePools: number; + totalUsers?: number; // Example of an additional stat + dailyVolume?: number; // Example of an additional stat +} + +export default function StatsPage() { + const [stats, setStats] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + setLoading(true); + fetchStats() + .then((data) => { + setStats(data); + setLoading(false); + }) + .catch((error) => { + console.error("Failed to fetch stats:", error); + setLoading(false); + // Optionally set an error state here + }); + }, []); + + if (loading) { + return ( +
+
+ + Loading Platform Statistics... +
+
+ ); + } + + if (!stats) { + return ( +
+

+ Failed to load platform statistics. Please try again later. +

+
+ ); + } + + const formatCurrency = (value: number) => { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(value); + }; + + return ( +
+
+
+ +

+ Platform Statistics +

+
+

+ An overview of key metrics and activity on the Blend protocol. +

+
+ +
+ + + + Total Value Locked (TVL) + + + + +
+ {formatCurrency(stats.totalTvl)} +
+

+ The total amount of assets locked in the protocol. +

+
+
+ + + + + Active Pools + + + + +
+ {stats.activePools} +
+

+ Number of currently active lending pools. +

+
+
+ + {stats.totalUsers !== undefined && ( + + + + Total Users + + + + +
+ {stats.totalUsers.toLocaleString()} +
+

+ Total unique users interacting with the protocol. +

+
+
+ )} + + {stats.dailyVolume !== undefined && ( + + + + 24h Trading Volume + + + + +
+ {formatCurrency(stats.dailyVolume)} +
+

+ Total trading volume in the last 24 hours. +

+
+
+ )} +
+
+ ); +} diff --git a/src/app/maintenance/page.tsx b/src/app/maintenance/page.tsx deleted file mode 100644 index 064cb94f..00000000 --- a/src/app/maintenance/page.tsx +++ /dev/null @@ -1,23 +0,0 @@ -"use client"; - -import { Bounded } from "@/components/layouts/Bounded"; -import CountdownTimer from "@/components/modules/maintenance/ui/CountdownTimer"; - -const Maintenance: React.FC = () => { - return ( - -
-

- Site Under Maintenance -

-

- We are making improvements to our platform to provide you with better - service. -

- -
-
- ); -}; - -export default Maintenance; diff --git a/src/app/page.tsx b/src/app/page.tsx index 202a0164..1f0a0bdc 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,6 @@ "use client"; import HomePage from "@/components/modules/auth/ui/pages/Home"; -import { GradientBackground } from "@/components/modules/dashboard/ui/pages/background/GradientBackground"; import { useRouter } from "next/navigation"; import { useEffect } from "react"; import { useWalletContext } from "@/providers/wallet.provider"; @@ -18,9 +17,9 @@ export default function Page() { }, [walletAddress, router]); return ( - + <> - + ); } diff --git a/src/components/layouts/header/Header.tsx b/src/components/layouts/header/Header.tsx index 6c636306..04f14849 100644 --- a/src/components/layouts/header/Header.tsx +++ b/src/components/layouts/header/Header.tsx @@ -3,51 +3,134 @@ import { useWallet } from "@/components/modules/auth/hooks/wallet.hook"; import { useWalletContext } from "@/providers/wallet.provider"; import { Button } from "@/components/ui/button"; -import { LogIn, LogOut, Wallet } from "lucide-react"; -import { SidebarTrigger } from "@/components/ui/sidebar"; +import { + LogIn, + LogOut, + Wallet, + FlaskConical, + Menu, + MessageSquare, + User, +} from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import Link from "next/link"; export function Header() { const { walletAddress } = useWalletContext(); const { handleConnect, handleDisconnect } = useWallet(); const truncateAddress = (address: string) => { + if (!address) return ""; return `${address.slice(0, 6)}...${address.slice(-4)}`; }; return ( -
- -
-
- {walletAddress ? ( - <> -
- - - {truncateAddress(walletAddress)} - -
+
+
+
+ + + TrustBridge + + +
+ +
+
+ {walletAddress ? ( + <> +
+ + + {truncateAddress(walletAddress)} + +
+ + + ) : ( - - ) : ( - - )} + )} +
+ +
+ + + + + + + + + Chat + + + + + + Profile + + + + +
diff --git a/src/components/layouts/header/HeaderHome.tsx b/src/components/layouts/header/HeaderHome.tsx index 47f92f89..0f84b676 100644 --- a/src/components/layouts/header/HeaderHome.tsx +++ b/src/components/layouts/header/HeaderHome.tsx @@ -1,20 +1,71 @@ "use client"; -import React from "react"; import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { FlaskConical, Wallet, Menu, LogIn, LogOut } from "lucide-react"; +import { useWallet } from "@/components/modules/auth/hooks/wallet.hook"; +import { useWalletContext } from "@/providers/wallet.provider"; + +export default function AppHeader() { + const { walletAddress } = useWalletContext(); + const { handleConnect, handleDisconnect } = useWallet(); + + const truncateAddress = (address: string) => { + return `${address.slice(0, 6)}...${address.slice(-4)}`; + }; -const HeaderHome: React.FC = () => { return ( -
-
- - - +
+
+
+ + + TrustBridge + +
+ +
+
+ {walletAddress ? ( + <> +
+ + + {truncateAddress(walletAddress)} + +
+ + + ) : ( + + )} +
+ +
); -}; - -export default HeaderHome; +} diff --git a/src/components/layouts/sidebar/hooks/useSidebar.hook.tsx b/src/components/layouts/sidebar/hooks/useSidebar.hook.tsx index b9aaf899..38c99830 100644 --- a/src/components/layouts/sidebar/hooks/useSidebar.hook.tsx +++ b/src/components/layouts/sidebar/hooks/useSidebar.hook.tsx @@ -4,7 +4,6 @@ import { useState } from "react"; import { usePathname } from "next/navigation"; import { LayoutDashboard, - CreditCard, MessageSquare, Settings, ShoppingCart, @@ -49,17 +48,17 @@ export function useTrustBridgeSidebar() { label: "Dashboard", active: pathname === "/dashboard", }, + { + href: "/dashboard/stats", + icon: , + label: "Stats", + active: pathname === "/dashboard/stats", + }, ], }, { section: "Financial", items: [ - { - href: "/dashboard/loans", - icon: , - label: "Loans", - active: pathname.startsWith("/dashboard/loans"), - }, { href: "/dashboard/marketplace", icon: , diff --git a/src/components/middleware.ts b/src/components/middleware.ts deleted file mode 100644 index d599e453..00000000 --- a/src/components/middleware.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; - -export function middleware(request: NextRequest) { - const maintenanceMode = process.env.NEXT_PUBLIC_MAINTENANCE_MODE === "true"; - - // Maintenance mode activated - if ( - maintenanceMode && - request.nextUrl.pathname !== "/maintenance" && - !request.nextUrl.pathname.startsWith("/_next") && - !request.nextUrl.pathname.startsWith("/static") - ) { - return NextResponse.redirect(new URL("/maintenance", request.url)); - } - - // Maintenance mode deactivated - if (!maintenanceMode && request.nextUrl.pathname === "/maintenance") { - return NextResponse.redirect(new URL("/", request.url)); - } - - return NextResponse.next(); -} - -export const config = { - matcher: ["/", "/:path*"], -}; diff --git a/src/components/modules/about-us/hooks/useAbout.hook.ts b/src/components/modules/about-us/hooks/useAbout.hook.ts deleted file mode 100644 index fb10654f..00000000 --- a/src/components/modules/about-us/hooks/useAbout.hook.ts +++ /dev/null @@ -1,66 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; - -// Types for About Us data -export interface TeamMember { - name: string; - role: string; -} - -export interface AboutData { - mission: string; - story: string; - team: TeamMember[]; - technology: string; -} - -// Mock API fetch function -async function fetchAboutData(): Promise { - return new Promise((resolve) => { - setTimeout(() => { - resolve({ - mission: - "To empower individuals and communities with direct financial access by removing traditional barriers, fees, and intermediaries through a trustless system.", - story: `TrustBridge was born in 2024 when a group of friends passionate about blockchain technologies came together with a common vision: to create a bridge of trust between capital and opportunity.\n\nBuilt on the Stellar blockchain, we chose this technology for its speed, low cost, and focus on financial inclusion, values that are at the core of our identity.`, - team: [ - { - name: "Josué Brenes", - role: "CEO & FullStack Developer", - }, - { - name: "Yuliana Gonzáles", - role: "BackEnd Developer", - }, - { - name: "Daniel Coto", - role: "FrontEnd Developer", - }, - ], - technology: - "We use the Stellar blockchain to provide fast, secure, and low-cost transactions. Our platform is designed to be accessible to both experienced cryptocurrency users and those new to this technology.", - }); - }, 800); - }); -} - -export function useAboutData() { - const [data, setData] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - fetchAboutData() - .then((res) => { - setData(res); - setLoading(false); - }) - .catch((error) => { - console.error("Failed to fetch about data:", error); - setError("Failed to load about data"); - setLoading(false); - }); - }, []); - - return { data, loading, error }; -} diff --git a/src/components/modules/about-us/ui/pages/AboutUs.tsx b/src/components/modules/about-us/ui/pages/AboutUs.tsx deleted file mode 100644 index 4c419f69..00000000 --- a/src/components/modules/about-us/ui/pages/AboutUs.tsx +++ /dev/null @@ -1,81 +0,0 @@ -"use client"; -import Link from "next/link"; -import { TeamMember, useAboutData } from "../../hooks/useAbout.hook"; - -export default function AboutPage() { - const { data, loading, error } = useAboutData(); - - return ( -
-
- - ← Back to Home - -

About Us

- - {loading && ( -
- Loading... -
- )} - - {error && ( -
{error}
- )} - - {data && ( - <> -
-

- Our Mission -

-

{data.mission}

-
- -
-

- Our Story -

-

- {data.story} -

-
- -
-

- Our Team -

-
- {data.team.map((member: TeamMember) => ( -
-
- - {member.name} - - - {member.role} - -
- ))} -
-
- -
-

- Blockchain Technology -

-

{data.technology}

-
- - )} -
-
- ); -} diff --git a/src/components/modules/auth/ui/pages/Home.tsx b/src/components/modules/auth/ui/pages/Home.tsx index 3835e7f8..93d4163c 100644 --- a/src/components/modules/auth/ui/pages/Home.tsx +++ b/src/components/modules/auth/ui/pages/Home.tsx @@ -1,111 +1,57 @@ "use client"; -import { useWallet } from "@/components/modules/auth/hooks/wallet.hook"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { useEffect } from "react"; -import { ArrowRight, Wallet, LogIn, LogOut } from "lucide-react"; +import { ArrowRight } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import Link from "next/link"; export default function HomePage() { - const { walletAddress } = useWalletContext(); - const { handleConnect, handleDisconnect } = useWallet(); - - useEffect(() => { - document.body.style.overflow = "hidden"; - return () => { - document.body.style.overflow = "auto"; - }; - }, []); - - const truncateAddress = (address: string) => { - return `${address.slice(0, 6)}...${address.slice(-4)}`; - }; - return ( -
-
-
-
-
- - Powered by Stellar Blockchain - - -

- - TrustBridge - - Decentralized Microloans -

- -

- Connecting lenders and borrowers through secure, transparent, - and efficient blockchain technology. Build trust, create - opportunity. -

-
- - {/* Mobile wallet address display */} - {walletAddress && ( -
- - - {truncateAddress(walletAddress)} - +
+
+
+
+
+
+ + Powered by Stellar Blockchain + + +

+ + TrustBridge + + + Decentralized Microloans + +

+ +

+ Connecting lenders and borrowers through secure, transparent, + and efficient blockchain technology. Build trust, create + opportunity. +

- )} - -
- {walletAddress ? ( - <> - {/* Desktop wallet address display */} -
- - - {truncateAddress(walletAddress)} - -
+
+ - - ) : ( - - )} - - - - + +
-
-
+
+
); } diff --git a/src/components/modules/blend/ui/cards/PoolCard.tsx b/src/components/modules/blend/ui/cards/PoolCard.tsx new file mode 100644 index 00000000..802d9c01 --- /dev/null +++ b/src/components/modules/blend/ui/cards/PoolCard.tsx @@ -0,0 +1,131 @@ +"use client"; +import Link from "next/link"; +import type React from "react"; + +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + ArrowRight, + Bitcoin, + CircleDollarSign, + Euro, + FlaskConical, + ChevronDown, + Briefcase, + Landmark, +} from "lucide-react"; + +export type PoolCardProps = { + id: string; + name: string; + state: string; // "active", "on ice", "deprecated" + reserves: string; // e.g., "$10.88M" - Mapped to "Supplied" + depositApy?: number; + borrowApy?: number; + supplied?: string; + borrowed?: string; + backstop?: string; + assetIcons?: React.ElementType[]; +}; + +export function PoolCard({ + id, + name, + reserves, // Used as "Supplied" + borrowApy, // Used as "Borrowed" + supplied, // Prefer this if available + borrowed, // Prefer this if available + backstop, // Prefer this if available + assetIcons = [CircleDollarSign, Euro, Bitcoin], +}: PoolCardProps) { + const displaySupplied = supplied || reserves; + const displayBorrowed = borrowed || (borrowApy ? `${borrowApy}% APY` : "N/A"); + const displayBackstop = backstop || "$0.00k"; + + return ( + + +
+ + + {name} + + + V2 + +
+ +
+ +
+
+

+ Supplied +

+

+ {displaySupplied} +

+
+
+

+ Borrowed +

+

+ {displayBorrowed} +

+
+
+

+ Backstop +

+

+ {displayBackstop} +

+
+
+
+ {assetIcons.map((IconComponent, index) => ( +
+ +
+ ))} +
+
+ +
+ {/* State badge can be here if needed, or removed if not in the target style */} + {/* {state} */} +
+ + +
+ + + +
+
+
+ ); +} diff --git a/src/components/modules/blend/ui/detail/PoolDetail.tsx b/src/components/modules/blend/ui/detail/PoolDetail.tsx new file mode 100644 index 00000000..762baceb --- /dev/null +++ b/src/components/modules/blend/ui/detail/PoolDetail.tsx @@ -0,0 +1,252 @@ +"use client"; +import { useEffect, useState } from "react"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { fetchPool, fetchUserInfo } from "@/lib/blend/helpers"; // Mocked helpers +import { + RefreshCw, + FlaskConical, + UserCircle, + Activity, + ShieldCheck, + Settings, +} from "lucide-react"; +import { PoolData, UserData } from "@/@types/pool.entity"; + +export function PoolDetail({ id }: { id: string }) { + const [loading, setLoading] = useState(true); + const [pool, setPool] = useState(null); + const [user, setUser] = useState(null); + + const load = async () => { + setLoading(true); + const p = await fetchPool(id); + const u = await fetchUserInfo(id); + setPool(p); + setUser( + u + ? { + ...u, + bTokens: Number(u.bTokens), + dTokens: Number(u.dTokens), + collateral: Number(u.collateral), + debt: Number(u.debt), + healthFactor: Number(u.healthFactor), + } + : null, + ); + setLoading(false); + }; + + useEffect(() => { + load(); + }, [id]); + + if (loading || !pool) { + return ( +
+ +

Loading pool details...

+
+ ); + } + + const tabItems = [ + { value: "general", label: "General", icon: FlaskConical }, + { value: "user", label: "User", icon: UserCircle }, + { value: "health", label: "Health", icon: Activity }, + { value: "backstop", label: "Backstop", icon: ShieldCheck }, + { value: "actions", label: "Actions", icon: Settings }, + ]; + + return ( +
+
+
+ +

+ {pool.name ? `${pool.name} Overview` : `Pool ${id} Overview`} +

+
+ +
+ + + {tabItems.map((tab) => ( + + + {tab.label} + + ))} + + + {tabItems.map((tab) => ( + + + + + + {tab.label} Details + + + + {tab.value === "general" && ( + <> +

+ + Assets: + {" "} + {pool.assets?.join(", ")} +

+

+ + Base Rate: + {" "} + {pool.interest?.base}% +

+ {pool.depositApy !== undefined && ( +

+ + Deposit APY: + {" "} + {pool.depositApy}% +

+ )} + {pool.borrowApy !== undefined && ( +

+ + Borrow APY: + {" "} + {pool.borrowApy}% +

+ )} + {pool.ltv !== undefined && ( +

+ + LTV: + {" "} + {pool.ltv}% +

+ )} + {pool.liquidationThreshold !== undefined && ( +

+ + Liquidation Threshold: + {" "} + {pool.liquidationThreshold}% +

+ )} +

+ + Risk Params: + {" "} + {JSON.stringify(pool.risk)} +

+ + )} + {tab.value === "user" && user && ( + <> +

+ + bTokens: + {" "} + {user.bTokens} +

+

+ + dTokens: + {" "} + {user.dTokens} +

+

+ + Collateral: + {" "} + {user.collateral} +

+

+ + Debt: + {" "} + {user.debt} +

+ + )} + {tab.value === "health" && user && ( +

+ + Health Factor: + {" "} + + {user.healthFactor} + +

+ )} + {tab.value === "backstop" && pool.backstop && ( + <> +

+ TVL:{" "} + {pool.backstop.tvl} +

+

+ + Withdrawal Queue: + {" "} + {pool.backstop.withdrawalPct}% +

+

+ + Reward Zone: + {" "} + {pool.backstop.rewardZone ? ( + Yes + ) : ( + No + )} +

+ + )} + {tab.value === "actions" && ( +
+ + +
+ )} + {(tab.value === "user" || tab.value === "health") && !user && ( +

+ No user data available for this pool. +

+ )} +
+
+
+ ))} +
+
+ ); +} diff --git a/src/components/modules/blend/ui/pages/PoolDetailPage.tsx b/src/components/modules/blend/ui/pages/PoolDetailPage.tsx new file mode 100644 index 00000000..89d6ee82 --- /dev/null +++ b/src/components/modules/blend/ui/pages/PoolDetailPage.tsx @@ -0,0 +1,10 @@ +"use client"; +import { PoolDetail } from "../detail/PoolDetail"; + +export function PoolDetailPage({ id }: { id: string }) { + return ( +
+ +
+ ); +} diff --git a/src/components/modules/blend/ui/pages/PoolsPage.tsx b/src/components/modules/blend/ui/pages/PoolsPage.tsx new file mode 100644 index 00000000..059e6e90 --- /dev/null +++ b/src/components/modules/blend/ui/pages/PoolsPage.tsx @@ -0,0 +1,38 @@ +"use client"; +import { useEffect, useState } from "react"; +import { PoolCard } from "../cards/PoolCard"; +import { fetchPools } from "@/lib/blend/helpers"; +import type { PoolData } from "@/@types/pool.entity"; + +export function PoolsPage() { + const [pools, setPools] = useState([]); + const [query, setQuery] = useState(""); + + useEffect(() => { + fetchPools().then(setPools).catch(console.error); + }, []); + + return ( +
+ setQuery(e.target.value)} + /> + {pools + .filter((p) => p.name.toLowerCase().includes(query.toLowerCase())) + .map((pool) => ( + + ))} +
+ ); +} diff --git a/src/components/modules/chat/hooks/chat.hook.ts b/src/components/modules/chat/hooks/chat.hook.ts deleted file mode 100644 index 84552ed0..00000000 --- a/src/components/modules/chat/hooks/chat.hook.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { useEffect, useState } from "react"; -import { fetchMessages, sendMessage } from "../lib/chat"; - -type ChatMessage = { - id: string; - from: string; - to: string; - content: string; - timestamp: number; - status?: string; -}; - -export const useChat = (walletA: string, walletB: string) => { - const [messages, setMessages] = useState([]); - - const loadMessages = async () => { - const msgs = await fetchMessages(walletA, walletB); - setMessages(msgs); - }; - - const handleSend = async (message: string) => { - await sendMessage(walletA, walletB, message); - await loadMessages(); - }; - - useEffect(() => { - loadMessages(); - }, [walletA, walletB]); - - return { messages, handleSend }; -}; diff --git a/src/components/modules/chat/hooks/use-all-chats.hook.ts b/src/components/modules/chat/hooks/use-all-chats.hook.ts deleted file mode 100644 index f29b0ffc..00000000 --- a/src/components/modules/chat/hooks/use-all-chats.hook.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { useEffect, useState } from "react"; -import { onSnapshot, query, collection, where } from "firebase/firestore"; -import { db } from "@/lib/firebase"; -import { ChatMessage, ChatWithMessages } from "@/@types/chat.entity"; - -export const useAllChats = (walletAddress: string) => { - const [chats, setChats] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - if (!walletAddress) { - setLoading(false); - return; - } - - let unsubscribe: (() => void) | undefined; - - const setupChats = async () => { - try { - setLoading(true); - setError(null); - - // Set up real-time listener for all chats - const chatsRef = collection(db, "chats"); - const q = query( - chatsRef, - where("participants", "array-contains", walletAddress), - ); - - unsubscribe = onSnapshot( - q, - async (snapshot) => { - const updatedChats = await Promise.all( - snapshot.docs.map(async (doc) => { - const chatData = doc.data() as Omit< - ChatWithMessages, - "id" | "messages" - >; - const messages: ChatMessage[] = []; - - return { - id: doc.id, - ...chatData, - messages, - } as ChatWithMessages; - }), - ); - - setChats(updatedChats); - setLoading(false); - }, - (err) => { - console.error("Error listening to chats:", err); - setError("Error listening to chats"); - setLoading(false); - }, - ); - } catch (err) { - console.error("Error setting up chats:", err); - setError("Error setting up chats"); - setLoading(false); - } - }; - - setupChats(); - - return () => { - if (unsubscribe) unsubscribe(); - }; - }, [walletAddress]); - - return { chats, loading, error }; -}; diff --git a/src/components/modules/chat/hooks/wallet-chat.hook.ts b/src/components/modules/chat/hooks/wallet-chat.hook.ts deleted file mode 100644 index 5dbbd167..00000000 --- a/src/components/modules/chat/hooks/wallet-chat.hook.ts +++ /dev/null @@ -1,74 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { listenToMessages, sendMessage } from "../lib/chat"; - -type ChatMessage = { - id: string; - from: string; - to: string; - content: string; - timestamp: number; - status?: "sent" | "delivered" | "read"; -}; - -export const useWalletChat = (walletA: string, walletB: string) => { - const [messages, setMessages] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - if ( - !walletA || - !walletB || - walletA === "[wallet]" || - walletB === "[wallet]" - ) { - setLoading(false); - return; - } - - let unsubscribe: (() => void) | undefined; - - const setupChat = async () => { - try { - setLoading(true); - setError(null); - - unsubscribe = await listenToMessages( - walletA, - walletB, - (newMessages: ChatMessage[]) => { - setMessages(newMessages); - setLoading(false); - }, - ); - } catch (err) { - console.error("Error setting up chat:", err); - setError("Error setting up chat"); - setLoading(false); - } - }; - - setupChat(); - - return () => { - if (unsubscribe) unsubscribe(); - }; - }, [walletA, walletB]); - - const send = async (content: string) => { - try { - setError(null); - await sendMessage(walletA, walletB, content); - // No necesitamos actualizar el estado local aquí porque listenToMessages - // se encargará de actualizar los mensajes en tiempo real - } catch (err) { - console.error("Error sending message:", err); - setError("Error sending message"); - throw err; - } - }; - - return { messages, sendMessage: send, loading, error }; -}; diff --git a/src/components/modules/chat/lib/chat.ts b/src/components/modules/chat/lib/chat.ts deleted file mode 100644 index dafe4762..00000000 --- a/src/components/modules/chat/lib/chat.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { UserChatData } from "@/@types/user.entity"; -import { db } from "@/lib/firebase"; -import { - collection, - addDoc, - query, - orderBy, - onSnapshot, - getDocs, - doc, - setDoc, - getDoc, - Unsubscribe, - where, - serverTimestamp, - Timestamp, -} from "firebase/firestore"; - -export type ChatMessage = { - id: string; - from: string; - to: string; - content: string; - timestamp: number; - status?: "sent" | "delivered" | "read"; -}; - -export const getChatId = (walletA: string, walletB: string) => { - if ( - !walletA || - !walletB || - walletA === "[wallet]" || - walletB === "[wallet]" - ) { - throw new Error("Invalid wallet addresses"); - } - const sortedWallets = [walletA, walletB].sort(); - return sortedWallets.join("_"); -}; - -export const initializeChat = async (walletA: string, walletB: string) => { - if ( - !walletA || - !walletB || - walletA === "[wallet]" || - walletB === "[wallet]" - ) { - throw new Error("Invalid wallet addresses"); - } - - const chatId = getChatId(walletA, walletB); - const chatDocRef = doc(db, "chats", chatId); - - try { - const chatDoc = await getDoc(chatDocRef); - - if (!chatDoc.exists()) { - await setDoc(chatDocRef, { - participants: [walletA, walletB].sort(), - createdAt: serverTimestamp(), - lastMessage: null, - lastMessageTime: null, - }); - } - - return chatId; - } catch (error) { - console.error("Error initializing chat:", error); - throw error; - } -}; - -export const sendMessage = async ( - from: string, - to: string, - content: string, -) => { - if (!from || !to || from === "[wallet]" || to === "[wallet]") { - throw new Error("Invalid wallet addresses"); - } - - try { - const chatId = await initializeChat(from, to); - const messagesCol = collection(db, "chats", chatId, "messages"); - - const messageData = { - from, - to, - content, - timestamp: serverTimestamp(), - status: "sent", - }; - - await addDoc(messagesCol, messageData); - - const chatDocRef = doc(db, "chats", chatId); - await setDoc( - chatDocRef, - { - lastMessage: content, - lastMessageTime: serverTimestamp(), - }, - { merge: true }, - ); - - return messageData; - } catch (error) { - console.error("Error sending message:", error); - throw error; - } -}; - -const convertTimestamp = (timestamp: Timestamp | Date | number): number => { - if (timestamp instanceof Timestamp) { - return timestamp.toMillis(); - } - if (timestamp instanceof Date) { - return timestamp.getTime(); - } - if (typeof timestamp === "number") { - return timestamp; - } - return Date.now(); -}; - -export const fetchMessages = async ( - walletA: string, - walletB: string, -): Promise => { - if ( - !walletA || - !walletB || - walletA === "[wallet]" || - walletB === "[wallet]" - ) { - return []; - } - - const chatId = getChatId(walletA, walletB); - const chatDocRef = doc(db, "chats", chatId); - const chatDoc = await getDoc(chatDocRef); - - if (!chatDoc.exists()) { - return []; - } - - const messagesCol = collection(db, "chats", chatId, "messages"); - const q = query(messagesCol, orderBy("timestamp", "asc")); - const snapshot = await getDocs(q); - - return snapshot.docs.map((doc) => { - const data = doc.data(); - return { - id: doc.id, - from: data.from, - to: data.to, - content: data.content, - timestamp: convertTimestamp(data.timestamp), - status: data.status || "sent", - }; - }); -}; - -export const listenToMessages = async ( - walletA: string, - walletB: string, - callback: (messages: ChatMessage[]) => void, -): Promise => { - if ( - !walletA || - !walletB || - walletA === "[wallet]" || - walletB === "[wallet]" - ) { - callback([]); - return () => {}; - } - - const chatId = getChatId(walletA, walletB); - const chatDocRef = doc(db, "chats", chatId); - const chatDoc = await getDoc(chatDocRef); - - if (!chatDoc.exists()) { - await setDoc(chatDocRef, { - participants: [walletA, walletB].sort(), - createdAt: serverTimestamp(), - lastMessage: null, - lastMessageTime: null, - }); - } - - const messagesCol = collection(db, "chats", chatId, "messages"); - const q = query(messagesCol, orderBy("timestamp", "asc")); - - const unsubscribe = onSnapshot( - q, - (snapshot) => { - const msgs: ChatMessage[] = snapshot.docs.map((doc) => { - const data = doc.data(); - return { - id: doc.id, - from: data.from, - to: data.to, - content: data.content, - timestamp: convertTimestamp(data.timestamp), - status: data.status || "sent", - }; - }); - callback(msgs); - }, - (error) => { - console.error("Error listening to messages:", error); - }, - ); - - return unsubscribe; -}; - -export const getUserChats = async (walletAddress: string) => { - if (!walletAddress || walletAddress === "[wallet]") { - return []; - } - - const chatsRef = collection(db, "chats"); - const q = query( - chatsRef, - where("participants", "array-contains", walletAddress), - ); - - const snapshot = await getDocs(q); - return snapshot.docs.map((doc) => { - const data = doc.data(); - return { - id: doc.id, - participants: data.participants, - createdAt: convertTimestamp(data.createdAt), - lastMessage: data.lastMessage, - lastMessageTime: convertTimestamp(data.lastMessageTime), - }; - }); -}; - -export const getUserData = async ( - walletAddress: string, -): Promise => { - if (!walletAddress || walletAddress === "[wallet]") { - return { - firstName: "Unknown", - lastName: "", - walletAddress: "", - }; - } - - try { - const userDocRef = doc(db, "users", walletAddress); - const userDoc = await getDoc(userDocRef); - - if (userDoc.exists()) { - const data = userDoc.data(); - return { - firstName: data.firstName || "Unknown", - lastName: data.lastName || "", - walletAddress: data.walletAddress || walletAddress, - }; - } - - return { - firstName: "Unknown", - lastName: "", - walletAddress, - }; - } catch (error) { - console.error("Error getting user data:", error); - return { - firstName: "Unknown", - lastName: "", - walletAddress, - }; - } -}; diff --git a/src/components/modules/chat/ui/components/chat-list.tsx b/src/components/modules/chat/ui/components/chat-list.tsx deleted file mode 100644 index 0053b1e0..00000000 --- a/src/components/modules/chat/ui/components/chat-list.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { useRouter, useParams } from "next/navigation"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Card } from "@/components/ui/card"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { ChatWithMessages } from "@/@types/chat.entity"; -import { formatAddress } from "@/lib/utils"; -import { cn } from "@/lib/utils"; -import { useEffect, useState } from "react"; -import { getUserData } from "../../lib/chat"; -import { UserChatData } from "@/@types/user.entity"; - -interface ChatListProps { - chats: ChatWithMessages[]; - currentWallet: string; - loading: boolean; -} - -export function ChatList({ chats, currentWallet, loading }: ChatListProps) { - const router = useRouter(); - const { wallet: activeWallet } = useParams(); - const [userData, setUserData] = useState>({}); - - useEffect(() => { - const fetchUserData = async () => { - const newUserData: Record = {}; - - for (const chat of chats) { - const otherParticipant = chat.participants.find( - (p) => p !== currentWallet, - ); - - if (otherParticipant && !userData[otherParticipant]) { - const data = await getUserData(otherParticipant); - newUserData[otherParticipant] = data; - } - } - - setUserData((prev) => ({ ...prev, ...newUserData })); - }; - - if (chats.length > 0) { - fetchUserData(); - } - }, [chats, currentWallet]); - - if (loading) { - return ( -
-
-
- ); - } - - if (chats.length === 0) { - return ( -
-

- No chats yet. Start a new conversation by entering a wallet address - above. -

-
- ); - } - - return ( - -
- {chats.map((chat) => { - const otherParticipant = chat.participants.find( - (p) => p !== currentWallet, - ); - const lastMessageTime = chat.lastMessageTime - ? new Date(chat.lastMessageTime).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - }) - : null; - const isActive = otherParticipant === activeWallet; - const user = userData[otherParticipant || ""]; - - return ( - router.push(`/dashboard/chat/${otherParticipant}`)} - > -
- - - - {user?.firstName?.slice(0, 2).toUpperCase() || - otherParticipant?.slice(0, 2).toUpperCase()} - {user?.lastName?.slice(0, 2).toUpperCase() || - otherParticipant?.slice(0, 2).toUpperCase()} - - -
-
-
-

- {user?.firstName} {user?.lastName} -

-

- {formatAddress(otherParticipant || "")} -

-
- {lastMessageTime && ( - {lastMessageTime} - )} -
-

- {chat.lastMessage || "No messages yet"} -

-
-
-
- ); - })} -
-
- ); -} diff --git a/src/components/modules/chat/ui/components/message-bubble.tsx b/src/components/modules/chat/ui/components/message-bubble.tsx deleted file mode 100644 index 7d61d761..00000000 --- a/src/components/modules/chat/ui/components/message-bubble.tsx +++ /dev/null @@ -1,70 +0,0 @@ -"use client"; - -import { cn } from "@/lib/utils"; -import { Check, CheckCheck, Clock } from "lucide-react"; - -interface Message { - id: string; - content: string; - sender: "user" | "other"; - timestamp: string; - status: "sent" | "delivered" | "read"; -} - -interface MessageBubbleProps { - message: Message; -} - -export function MessageBubble({ message }: MessageBubbleProps) { - const isUser = message.sender === "user"; - - const getStatusIcon = () => { - switch (message.status) { - case "sent": - return ; - case "delivered": - return ; - case "read": - return ; - default: - return ; - } - }; - - return ( -
-
-
- {message.content} -
-
- {message.timestamp} - {isUser && ( - - {getStatusIcon()} - - )} -
-
-
- ); -} diff --git a/src/components/modules/chat/ui/dialogs/ChatDialog.tsx b/src/components/modules/chat/ui/dialogs/ChatDialog.tsx deleted file mode 100644 index 5a29160c..00000000 --- a/src/components/modules/chat/ui/dialogs/ChatDialog.tsx +++ /dev/null @@ -1,325 +0,0 @@ -"use client"; - -import { useState, useRef, useEffect } from "react"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Send, Menu, ArrowLeft, Copy, Check } from "lucide-react"; -import { MessageBubble } from "../components/message-bubble"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { useParams, useRouter } from "next/navigation"; -import { useWalletChat } from "../../hooks/wallet-chat.hook"; -import { useAllChats } from "../../hooks/use-all-chats.hook"; -import { ChatList } from "../components/chat-list"; -import { toast } from "sonner"; -import { getUserData } from "../../lib/chat"; -import { UserChatData } from "@/@types/user.entity"; - -export function ChatDialog() { - const { walletAddress } = useWalletContext(); - const { wallet: otherWallet } = useParams(); - const [message, setMessage] = useState(""); - const [targetWallet, setTargetWallet] = useState(""); - const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); - const [isSending, setIsSending] = useState(false); - const [copiedAddress, setCopiedAddress] = useState(false); - const [userData, setUserData] = useState(null); - const messageEndRef = useRef(null); - const inputRef = useRef(null); - const router = useRouter(); - - const { chats, loading: chatsLoading } = useAllChats(walletAddress!); - const { - messages, - sendMessage, - loading: messagesLoading, - error, - } = useWalletChat(walletAddress!, otherWallet as string); - - useEffect(() => { - if (error) { - toast.error(error); - } - }, [error]); - - useEffect(() => { - const fetchUserData = async () => { - if (otherWallet) { - const data = await getUserData(otherWallet as string); - setUserData(data); - } - }; - - fetchUserData(); - }, [otherWallet]); - - const handleSendMessage = async () => { - if (!message.trim() || isSending) return; - - try { - setIsSending(true); - await sendMessage(message); - setMessage(""); - inputRef.current?.focus(); - } catch { - toast.error("Failed to send message"); - } finally { - setIsSending(false); - } - }; - - const handleStartChat = () => { - if (!targetWallet.trim()) return; - router.push(`/dashboard/chat/${targetWallet}`); - setTargetWallet(""); - }; - - const copyAddress = async (address: string) => { - try { - await navigator.clipboard.writeText(address); - setCopiedAddress(true); - toast.success("Address copied to clipboard"); - setTimeout(() => setCopiedAddress(false), 2000); - } catch { - toast.error("Failed to copy address"); - } - }; - - const formatAddress = (address: string) => { - if (!address) return ""; - return `${address.slice(0, 6)}...${address.slice(-4)}`; - }; - - if (!walletAddress) { - return ( -
- -

- Wallet not connected. -

-
-
- ); - } - - const loading = chatsLoading || messagesLoading; - - return ( -
- {/* Mobile Menu Button */} - - - {/* Chat Window */} -
- {/* Chat List Sidebar */} -
- - -
- setTargetWallet(e.target.value)} - placeholder="Enter wallet address to start new chat" - className="flex-1" - /> - -
-
-
- - -
- - {/* Main Chat Content */} -
- {!otherWallet ? ( -
- -

- Select a chat or start a new conversation. -

-
-
- ) : ( - - {/* Header */} - -
- - -
- - - - {userData?.firstName?.slice(0, 2).toUpperCase() || - (otherWallet as string).slice(0, 2).toUpperCase()} - {userData?.lastName?.slice(0, 2).toUpperCase()} - - -
-
- -
-
-

- {userData?.firstName || - formatAddress(otherWallet as string)}{" "} - {userData?.lastName} -

- -
-
- - Wallet Chat - - - {formatAddress(otherWallet as string)} - - - {messages.length} messages - -
-
-
-
- - {/* Messages */} - - -
- {loading && messages.length === 0 ? ( -
-
-
- ) : messages.length === 0 ? ( -
-
- -
-

- Start the conversation -

-

- Send your first message to{" "} - {userData?.firstName || - formatAddress(otherWallet as string)}{" "} - {userData?.lastName} - to begin chatting. -

-
- ) : ( - messages.map((msg) => ( - - )) - )} -
-
- - - - {/* Input */} -
-
-
- setMessage(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter" && !e.shiftKey) { - e.preventDefault(); - handleSendMessage(); - } - }} - disabled={isSending} - className="pr-12 bg-background border-border focus:ring-emerald-600 focus:border-emerald-600" - /> - {message.trim() && ( -
- {message.length}/500 -
- )} -
- -
-
- Press Enter to send, Shift + Enter for new line - {error && ( - Failed to send message - )} -
-
- - )} -
-
-
- ); -} diff --git a/src/components/modules/dashboard/hooks/useDashboard.hook.ts b/src/components/modules/dashboard/hooks/useDashboard.hook.ts index 75e2879e..13060ed8 100644 --- a/src/components/modules/dashboard/hooks/useDashboard.hook.ts +++ b/src/components/modules/dashboard/hooks/useDashboard.hook.ts @@ -3,7 +3,7 @@ import { useState, useEffect } from "react"; import { useWalletContext } from "@/providers/wallet.provider"; import { useUserContext } from "@/providers/user.provider"; -import { getUserChats } from "@/components/modules/chat/lib/chat"; + import { UserProfile } from "@/@types/user.entity"; interface DashboardData { @@ -28,16 +28,6 @@ export function useDashboard(): DashboardData { setChatsLoading(false); return; } - - try { - const chats = await getUserChats(address); - setChatCount(chats.length); - } catch (err) { - console.error("Error loading chats:", err); - setChatCount(0); - } finally { - setChatsLoading(false); - } }; loadChats(); diff --git a/src/components/modules/dashboard/ui/pages/DashboardPage.tsx b/src/components/modules/dashboard/ui/pages/DashboardPage.tsx index 51c57241..3e12f216 100644 --- a/src/components/modules/dashboard/ui/pages/DashboardPage.tsx +++ b/src/components/modules/dashboard/ui/pages/DashboardPage.tsx @@ -1,307 +1,224 @@ "use client"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; -import { Badge } from "@/components/ui/badge"; import { + Accordion, + AccordionContent, + AccordionItem, +} from "@/components/ui/accordion"; +import { + AlertCircle, Wallet, User, - MessageCircle, - MapPin, - Phone, + ArrowRight, + FlaskConical, CreditCard, Shield, - Activity, - CheckCircle2, + MapPin, + Phone, } from "lucide-react"; import { useDashboard } from "../../hooks/useDashboard.hook"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { useEffect, useState } from "react"; +import { fetchPools } from "@/lib/blend/helpers"; +import { PoolCard } from "@/components/modules/blend/ui/cards/PoolCard"; +import { PoolData } from "@/@types/pool.entity"; export function DashboardOverview() { - const { loading, profile, address, walletName, chatCount } = useDashboard(); + const { loading, profile, address, walletName } = useDashboard(); + const [pools, setPools] = useState([]); - if (loading) { - return ( -
- - -
- {[...Array(3)].map((_, i) => ( - - - - - - - - - - - ))} -
-
- ); - } + useEffect(() => { + fetchPools().then(setPools).catch(console.error); + }, []); const formatAddress = (addr: string) => { if (!addr) return "Not connected"; return `${addr.slice(0, 6)}...${addr.slice(-4)}`; }; - return ( -
- {/* Header */} -
-
-
-

- Dashboard Overview -

-
-

- Your account summary, wallet information, and activity overview. -

-
- - {/* Main Cards Grid */} -
- {/* Wallet Information Card */} - -
- - -
- -
- Wallet Information -
-
- -
-
- - - Address - -
- {address ? ( - <> - - - Connected - - - ) : ( - - Disconnected - - )} -
-
- -
-

- {formatAddress(address || "")} -

-
- - {walletName && ( -
- - - Wallet Type - - {walletName} -
- )} + if (loading) { + return ( +
+ + + + +
+ +
- -
- - {/* User Profile Card */} - -
- - -
- -
- User Profile -
-
- - {profile ? ( -
-
- - Full Name - - - {profile.firstName} {profile.lastName} - -
- -
- - - Country - - {profile.country} -
- -
- - - Phone - - {profile.phoneNumber} -
- -
- - - Profile Complete - -
-
- ) : ( -
-
- -
-

- No profile data available -

- - Setup Required - -
- )} -
- - - {/* Activity Summary Card */} - -
- - -
- -
- Activity Summary -
- -
-
- - - Total Chats - -
- - {chatCount} - -
-
- -
-
- Chat Activity - - Active - -
-

- You have participated in {chatCount} conversation - {chatCount !== 1 ? "s" : ""} on the platform. -

-
- -
-
-

- {chatCount > 0 ? "100%" : "0%"} -

-

Engagement

-
-
-

- {chatCount > 5 ? "High" : chatCount > 0 ? "Medium" : "Low"} -

-

- Activity Level -

-
-
-
+ + {" "} + {/* Adjusted grid to 2 cols */} + + + + +
+ ); + } - {/* Additional Info Section */} - {(profile || address) && ( - - - Account Status - - -
-
-
-
-

Wallet Connection

-

- {address ? "Connected and verified" : "Not connected"} -

-
-
+ return ( +
+ {!address && ( + + + Please connect your wallet + + The wallet address does not exist on the network. Please fund your + account! + + + )} -
-
-
-

Profile Setup

-

- {profile - ? "Complete profile information" - : "Profile setup required"} -

+ + +
+ + + Account Overview + +
+
+ + +
+
+

Wallet Status

+

+ {address ? "Connected" : "Disconnected"} +

+
+
+

Profile Status

+

+ {profile ? "Complete" : "Incomplete"} +

+
+
+ + + + + {/* Wallet Details */} +
+

+ Wallet + Information +

+
+
+ + Address + + + {formatAddress(address || "")} + +
+ {walletName && ( +
+ + Wallet Type + + + {walletName} + +
+ )} +
-
- -
-
0 ? "bg-emerald-800" : "bg-gray-400"}`} - /> -
-

Platform Activity

-

- {chatCount > 0 ? "Active participant" : "New to platform"} -

+ {/* Profile Details */} +
+

+ User Profile +

+ {profile ? ( +
+
+ Full Name + + {profile.firstName} {profile.lastName} + +
+
+ + Country + + + {profile.country} + +
+
+ + Phone + + + {profile.phoneNumber} + +
+
+ ) : ( +

+ No profile data available. Please complete your profile. +

+ )}
-
+ + + + + + +
+
+
- - - )} +
+ +
+
+ + Dashboard + +
+ + +
+

Markets

+ {pools.map((p) => ( + + ))} +
); } diff --git a/src/components/modules/dashboard/ui/pages/background/GradientBackground.tsx b/src/components/modules/dashboard/ui/pages/background/GradientBackground.tsx deleted file mode 100644 index 9fda515f..00000000 --- a/src/components/modules/dashboard/ui/pages/background/GradientBackground.tsx +++ /dev/null @@ -1,50 +0,0 @@ -"use client"; - -import { cn } from "@/lib/utils"; -import type { ReactNode } from "react"; - -interface GradientBackgroundProps { - children: ReactNode; - className?: string; - opacity?: number; - primaryColor?: string; - secondaryColor?: string; - primaryPosition?: string; - secondaryPosition?: string; - primarySize?: string; - secondarySize?: string; - primaryBlur?: string; - secondaryBlur?: string; -} - -export function GradientBackground({ - children, - className, - opacity = 30, - primaryColor = "bg-emerald-800", - secondaryColor = "bg-emerald-800", - primaryPosition = "-top-40 -right-40", - secondaryPosition = "top-1/2 -left-40", - primarySize = "w-80 h-80", - secondarySize = "w-80 h-80", - primaryBlur = "blur-3xl", - secondaryBlur = "blur-3xl", -}: GradientBackgroundProps) { - return ( -
-
-
-
-
- - {/* Content */} -
{children}
-
- ); -} diff --git a/src/components/modules/escrows/constants/initialize-steps.constant.ts b/src/components/modules/escrows/constants/initialize-steps.constant.ts deleted file mode 100644 index 74625df6..00000000 --- a/src/components/modules/escrows/constants/initialize-steps.constant.ts +++ /dev/null @@ -1,22 +0,0 @@ -export const steps = [ - { - id: "basic", - title: "Basic Information", - description: "Enter the basic details about your escrow", - }, - { - id: "financial", - title: "Financial Details", - description: "Set up the financial parameters of your escrow", - }, - { - id: "roles", - title: "Roles Configuration", - description: "Configure all the roles involved in the escrow", - }, - { - id: "milestones", - title: "Milestones Setup", - description: "Define the milestones for your escrow", - }, -]; diff --git a/src/components/modules/escrows/constants/trustline.constant.ts b/src/components/modules/escrows/constants/trustline.constant.ts deleted file mode 100644 index 82fe6dcb..00000000 --- a/src/components/modules/escrows/constants/trustline.constant.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * - * The allows the user to interact with some tokens, in this case, we're using USDC and EURC. But you can add more trustlines. - * - */ -export const trustlines = [ - { - name: "USDC", - address: "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA", - decimals: 10000000, - }, - { - name: "EURC", - address: "GB3Q6QDZYTHWT7E5PVS3W7FUT5GVAFC5KSZFFLPU25GO7VTC3NM2ZTVO", - decimals: 10000000, - }, - - // you can add more trustlines here -]; diff --git a/src/components/modules/escrows/hooks/change-milestone-flag-form.hook.ts b/src/components/modules/escrows/hooks/change-milestone-flag-form.hook.ts deleted file mode 100644 index 6dba1c46..00000000 --- a/src/components/modules/escrows/hooks/change-milestone-flag-form.hook.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; -import { formSchema } from "../schemas/change-milestone-flag-form.schema"; -import { toast } from "sonner"; -import { signTransaction } from "../../auth/helpers/stellar-wallet-kit.helper"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { WalletError } from "@/@types/errors.entity"; -import { - useChangeMilestoneApprovedFlag, - useSendTransaction, -} from "@trustless-work/escrow/hooks"; -import { - ChangeMilestoneApprovedFlagPayload, - Escrow, - EscrowRequestResponse, - Milestone, -} from "@trustless-work/escrow/types"; -import { useEscrowContext } from "@/providers/escrow.provider"; - -export const useChangeMilestoneFlagForm = () => { - const { escrow, setEscrow } = useEscrowContext(); - const [loading, setLoading] = useState(false); - const [response, setResponse] = useState(null); - const { walletAddress } = useWalletContext(); - const { changeMilestoneApprovedFlag } = useChangeMilestoneApprovedFlag(); - const { sendTransaction } = useSendTransaction(); - - // Default milestones if escrow is undefined - const milestones = escrow?.milestones || [ - { description: "Initial setup", status: "pending" }, - { description: "Development phase", status: "pending" }, - ]; - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - contractId: escrow?.contractId || "CAZ6UQX7DEMO123", - milestoneIndex: "", - newFlag: true, - approver: escrow?.roles.approver || "GAPPROVER123456789", - }, - }); - - const onSubmit = async (payload: ChangeMilestoneApprovedFlagPayload) => { - setLoading(true); - setResponse(null); - - try { - /** - * API call by using the trustless work hooks - * @Note: - * - We need to pass the payload to the changeMilestoneApprovedFlag function - * - The result will be an unsigned transaction - */ - const { unsignedTransaction } = - await changeMilestoneApprovedFlag(payload); - - if (!unsignedTransaction) { - throw new Error( - "Unsigned transaction is missing from changeMilestoneApprovedFlag response.", - ); - } - - /** - * @Note: - * - We need to sign the transaction using your private key - * - The result will be a signed transaction - */ - const signedXdr = await signTransaction({ - unsignedTransaction, - address: walletAddress || "", - }); - - if (!signedXdr) { - throw new Error("Signed transaction is missing."); - } - - /** - * @Note: - * - We need to send the signed transaction to the API - * - The data will be an SendTransactionResponse - */ - const data = await sendTransaction({ - signedXdr, - returnEscrowDataIsRequired: false, - }); - - /** - * @Responses: - * data.status === "SUCCESS" - * - Escrow updated successfully - * - Set the escrow in the context - * - Show a success toast - * - * data.status == "ERROR" - * - Show an error toast - */ - if (data.status === "SUCCESS" && escrow) { - const escrowUpdated: Escrow = { - ...escrow, - milestones: escrow!.milestones.map((milestone: Milestone, index) => - index === Number(payload.milestoneIndex) - ? { ...milestone, approvedFlag: payload.newFlag } - : milestone, - ), - }; - - setEscrow(escrowUpdated); - - toast.success( - `Milestone index - ${payload.milestoneIndex} has been approved`, - ); - setResponse(data); - form.reset(); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - return { form, milestones, loading, response, onSubmit }; -}; diff --git a/src/components/modules/escrows/hooks/change-milestone-status-form.hook.ts b/src/components/modules/escrows/hooks/change-milestone-status-form.hook.ts deleted file mode 100644 index 99df133a..00000000 --- a/src/components/modules/escrows/hooks/change-milestone-status-form.hook.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; -import { formSchema } from "../schemas/change-milestone-status-form.schema"; -import { toast } from "sonner"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { signTransaction } from "../../auth/helpers/stellar-wallet-kit.helper"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { - useChangeMilestoneStatus, - useSendTransaction, -} from "@trustless-work/escrow/hooks"; -import { - ChangeMilestoneStatusPayload, - Escrow, - EscrowRequestResponse, - Milestone, -} from "@trustless-work/escrow/types"; -import { useEscrowContext } from "@/providers/escrow.provider"; -import { WalletError } from "@/@types/errors.entity"; - -export const useChangeMilestoneStatusForm = () => { - const { escrow } = useEscrowContext(); - const { setEscrow } = useEscrowContext(); - const [loading, setLoading] = useState(false); - const [response, setResponse] = useState(null); - const { walletAddress } = useWalletContext(); - const { changeMilestoneStatus } = useChangeMilestoneStatus(); - const { sendTransaction } = useSendTransaction(); - - const milestones = escrow?.milestones || [ - { description: "Initial setup", status: "pending" }, - { description: "Development phase", status: "pending" }, - ]; - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - contractId: escrow?.contractId || "", - milestoneIndex: "", - newStatus: "", - evidence: "", - serviceProvider: escrow?.roles.serviceProvider || "", - }, - }); - - const onSubmit = async (payload: ChangeMilestoneStatusPayload) => { - setLoading(true); - setResponse(null); - - try { - /** - * API call by using the trustless work hooks - * @Note: - * - We need to pass the payload to the changeMilestoneStatus function - * - The result will be an unsigned transaction - */ - const { unsignedTransaction } = await changeMilestoneStatus(payload); - - if (!unsignedTransaction) { - throw new Error( - "Unsigned transaction is missing from changeMilestoneStatus response.", - ); - } - - /** - * @Note: - * - We need to sign the transaction using your private key - * - The result will be a signed transaction - */ - const signedXdr = await signTransaction({ - unsignedTransaction, - address: walletAddress || "", - }); - - if (!signedXdr) { - throw new Error("Signed transaction is missing."); - } - - /** - * @Note: - * - We need to send the signed transaction to the API - * - The data will be an SendTransactionResponse - */ - const data = await sendTransaction({ - signedXdr, - returnEscrowDataIsRequired: false, - }); - - /** - * @Responses: - * data.status === "SUCCESS" - * - Escrow updated successfully - * - Set the escrow in the context - * - Show a success toast - * - * data.status == "ERROR" - * - Show an error toast - */ - if (data.status === "SUCCESS" && escrow) { - const escrowUpdated: Escrow = { - ...escrow, - milestones: escrow!.milestones.map((milestone: Milestone, index) => - index === Number(payload.milestoneIndex) - ? { - ...milestone, - status: payload.newStatus, - evidence: payload.evidence || "", - } - : milestone, - ), - }; - - setEscrow(escrowUpdated); - - toast.success( - `Milestone index - ${payload.milestoneIndex} updated to ${payload.newStatus}`, - ); - setResponse(data); - form.reset(); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - return { form, milestones, loading, response, onSubmit }; -}; diff --git a/src/components/modules/escrows/hooks/fund-escrow-form.hook.ts b/src/components/modules/escrows/hooks/fund-escrow-form.hook.ts deleted file mode 100644 index 10b640b0..00000000 --- a/src/components/modules/escrows/hooks/fund-escrow-form.hook.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { useEscrowContext } from "@/providers/escrow.provider"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; -import { formSchema } from "../schemas/fund-escrow-form.schema"; -import { toast } from "sonner"; -import { signTransaction } from "../../auth/helpers/stellar-wallet-kit.helper"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { WalletError } from "@/@types/errors.entity"; -import { - useFundEscrow, - useSendTransaction, -} from "@trustless-work/escrow/hooks"; -import { - Escrow, - EscrowRequestResponse, - FundEscrowPayload, -} from "@trustless-work/escrow/types"; - -export const useFundEscrowForm = () => { - const { escrow } = useEscrowContext(); - const { setEscrow } = useEscrowContext(); - const { walletAddress } = useWalletContext(); - const [loading, setLoading] = useState(false); - const [response, setResponse] = useState(null); - const [error, setError] = useState(null); - const { fundEscrow } = useFundEscrow(); - const { sendTransaction } = useSendTransaction(); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - contractId: escrow?.contractId || "", - amount: escrow?.amount?.toString() || "1000", - signer: walletAddress || "Connect your wallet to get your address", - }, - }); - - const onSubmit = async (payload: FundEscrowPayload) => { - setLoading(true); - setError(null); - setResponse(null); - - try { - const { unsignedTransaction } = await fundEscrow(payload); - - if (!unsignedTransaction) { - throw new Error( - "Unsigned transaction is missing from fundEscrow response.", - ); - } - - const signedXdr = await signTransaction({ - unsignedTransaction, - address: walletAddress || "", - }); - - if (!signedXdr) { - throw new Error("Signed transaction is missing."); - } - - const data = await sendTransaction({ - signedXdr, - returnEscrowDataIsRequired: false, - }); - - if (data.status === "SUCCESS" && escrow) { - const escrowUpdated: Escrow = { - ...escrow, - balance: - escrow?.balance && Number(escrow.balance) > 0 - ? (Number(escrow.balance) + Number(payload.amount)).toString() - : payload.amount, - }; - - setEscrow(escrowUpdated); - - toast.success("Escrow Funded"); - setResponse(data); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - return { form, loading, response, error, onSubmit }; -}; diff --git a/src/components/modules/escrows/hooks/get-escrow-form.hook.ts b/src/components/modules/escrows/hooks/get-escrow-form.hook.ts deleted file mode 100644 index 1632f3b2..00000000 --- a/src/components/modules/escrows/hooks/get-escrow-form.hook.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { formSchema } from "../schemas/get-escrow-form.schema"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { useEscrowContext } from "@/providers/escrow.provider"; -import { useState } from "react"; -import { z } from "zod"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { toast } from "sonner"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { WalletError } from "@/@types/errors.entity"; -import { Escrow, GetEscrowParams } from "@trustless-work/escrow/types"; -import { useGetEscrow } from "@trustless-work/escrow/hooks"; - -export const useGetEscrowForm = () => { - const { walletAddress } = useWalletContext(); - const { escrow, setEscrow } = useEscrowContext(); - const [loading, setLoading] = useState(false); - const [response, setResponse] = useState(null); - const [error, setError] = useState(null); - const { getEscrow, escrow: currentEscrow } = useGetEscrow(); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - contractId: escrow?.contractId || "", - signer: walletAddress || "Connect your wallet to get your address", - }, - }); - - const onSubmit = async (payload: GetEscrowParams) => { - setLoading(true); - setError(null); - setResponse(null); - - try { - /** - * API call by using the trustless work hooks - * @Note: - * - We need to pass the payload to the getEscrow function - * - The result will be an Escrow - */ - await getEscrow(payload); - - if (!currentEscrow) { - throw new Error("Escrow not found"); - } - - /** - * @Responses: - * escrow !== null - * - Escrow received successfully - * - Set the escrow in the context - * - Show a success toast - * - * escrow === null - * - Show an error toast - */ - if (currentEscrow) { - setEscrow({ ...currentEscrow, contractId: payload.contractId }); - setResponse(currentEscrow); - toast.success("Escrow Received"); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - return { form, loading, response, error, onSubmit }; -}; diff --git a/src/components/modules/escrows/hooks/get-multiple-escrow-balances-form.hook.ts b/src/components/modules/escrows/hooks/get-multiple-escrow-balances-form.hook.ts deleted file mode 100644 index 9a7fed70..00000000 --- a/src/components/modules/escrows/hooks/get-multiple-escrow-balances-form.hook.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { useWalletContext } from "@/providers/wallet.provider"; -import { useState } from "react"; -import { useForm, useFieldArray } from "react-hook-form"; -import { z } from "zod"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { formSchema } from "../schemas/get-multiple-escrow-balances-form.schema"; -import { toast } from "sonner"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { WalletError } from "@/@types/errors.entity"; -import { - EscrowRequestResponse, - GetBalanceParams, -} from "@trustless-work/escrow/types"; -import { GetEscrowBalancesResponse } from "@trustless-work/escrow/types"; -import { useGetMultipleEscrowBalances } from "@trustless-work/escrow/hooks"; - -type FormData = z.infer; - -export const useGetMultipleEscrowBalancesForm = () => { - const { walletAddress } = useWalletContext(); - const [loading, setLoading] = useState(false); - const [response, setResponse] = useState< - EscrowRequestResponse | GetEscrowBalancesResponse[] | null - >(null); - const { getMultipleBalances, balances } = useGetMultipleEscrowBalances(); - - const form = useForm({ - resolver: zodResolver(formSchema), - defaultValues: { - signer: walletAddress || "", - addresses: [{ value: "" }], - }, - }); - - const { fields, append, remove } = useFieldArray({ - control: form.control, - name: "addresses", - }); - - const onSubmit = async (payload: FormData) => { - setLoading(true); - setResponse(null); - - // Transform the payload to the correct format - const transformedData: GetBalanceParams = { - addresses: payload.addresses.map((a) => a.value), - signer: payload.signer, - }; - - try { - /** - * API call by using the trustless work hooks - * @Note: - * - We need to pass the payload to the getMultipleBalances function - * - The result will be multiple escrow balances - */ - await getMultipleBalances(transformedData); - - if (!balances) { - throw new Error("Escrow not found"); - } - - /** - * @Responses: - * balances !== null - * - Escrow balances received successfully - * - Set the response - * - Show a success toast - * - * balances === null - * - Show an error toast - */ - if (balances) { - setResponse(balances); - toast.success("Escrow Balances Received"); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - return { form, loading, response, fields, append, remove, onSubmit }; -}; diff --git a/src/components/modules/escrows/hooks/initialize-escrow-form.hook.ts b/src/components/modules/escrows/hooks/initialize-escrow-form.hook.ts deleted file mode 100644 index fd51d0c1..00000000 --- a/src/components/modules/escrows/hooks/initialize-escrow-form.hook.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { useWalletContext } from "@/providers/wallet.provider"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { formSchema } from "../schemas/initialize-escrow-form.schema"; -import { toast } from "sonner"; -import { useEscrowContext } from "@/providers/escrow.provider"; -import { useTabsContext } from "@/providers/tabs.provider"; -import { trustlines } from "../constants/trustline.constant"; -import { z } from "zod"; -import { Resolver } from "react-hook-form"; -import { steps } from "../constants/initialize-steps.constant"; -import { buildEscrowFromResponse } from "../../../../helpers/build-escrow-from-response.helper"; -import { signTransaction } from "../../auth/helpers/stellar-wallet-kit.helper"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { WalletError } from "@/@types/errors.entity"; -import { - useInitializeEscrow as useInitializeEscrowHook, - useSendTransaction, -} from "@trustless-work/escrow/hooks"; -import { - InitializeEscrowPayload, - InitializeEscrowResponse, - Trustline, -} from "@trustless-work/escrow/types"; - -type FormValues = z.infer; - -export const useInitializeEscrow = () => { - const [currentStep, setCurrentStep] = useState(0); - const [loading, setLoading] = useState(false); - const [response, setResponse] = useState( - null, - ); - const { walletAddress } = useWalletContext(); - const { setEscrow } = useEscrowContext(); - const { setActiveTab } = useTabsContext(); - - const { deployEscrow } = useInitializeEscrowHook(); - const { sendTransaction } = useSendTransaction(); - - const form = useForm({ - resolver: zodResolver(formSchema) as Resolver, - defaultValues: { - signer: walletAddress || "", - engagementId: "", - title: "", - description: "", - amount: "", - platformFee: "", - receiverMemo: 0, - roles: { - approver: "", - serviceProvider: "", - platformAddress: "", - releaseSigner: "", - disputeResolver: "", - receiver: "", - }, - trustline: { - address: "", - decimals: 10000000, - }, - milestones: [ - { - description: "", - status: "pending", - evidence: "", - approvedFlag: false, - }, - ], - }, - mode: "onChange", - }); - - const trustlinesOptions = trustlines.map( - (trustline: Trustline & { name?: string }) => ({ - value: trustline.address, - label: trustline.name, - }), - ); - - const addMilestone = () => { - const currentMilestones = form.getValues("milestones"); - form.setValue("milestones", [ - ...currentMilestones, - { description: "", status: "pending", evidence: "", approvedFlag: false }, - ]); - }; - - const removeMilestone = (index: number) => { - const currentMilestones = form.getValues("milestones"); - if (currentMilestones.length > 1) { - form.setValue( - "milestones", - currentMilestones.filter((_, i) => i !== index), - ); - } - }; - - const loadTemplate = () => { - form.setValue("title", "Sample TW Escrow"); - form.setValue( - "description", - "This is a sample TW escrow for testing purposes", - ); - form.setValue("engagementId", "ENG12345"); - form.setValue("amount", "50"); - form.setValue("platformFee", "5"); - form.setValue("roles.approver", walletAddress || ""); - form.setValue("roles.serviceProvider", walletAddress || ""); - form.setValue("roles.platformAddress", walletAddress || ""); - form.setValue("roles.releaseSigner", walletAddress || ""); - form.setValue("roles.disputeResolver", walletAddress || ""); - form.setValue("roles.receiver", walletAddress || ""); - form.setValue("receiverMemo", 90909090); - form.setValue( - "trustline.address", - trustlines.find((t) => t.name === "USDC")?.address || "", - ); - form.setValue("milestones", [ - { - description: "Initial milestone", - status: "pending", - evidence: "", - approvedFlag: false, - }, - { - description: "Second milestone", - status: "pending", - evidence: "", - approvedFlag: false, - }, - { - description: "Final milestone", - status: "pending", - evidence: "", - approvedFlag: false, - }, - ]); - }; - - const onSubmit = async (payload: InitializeEscrowPayload) => { - setLoading(true); - setResponse(null); - - try { - // This is the final payload that will be sent to the API - const finalPayload: InitializeEscrowPayload = { - ...payload, - receiverMemo: payload.receiverMemo ?? 0, - signer: walletAddress || "", - }; - - /** - * API call by using the trustless work hooks - * @Note: - * - We need to pass the payload to the deployEscrow function - * - The result will be an unsigned transaction - */ - const { unsignedTransaction } = await deployEscrow(finalPayload); - - if (!unsignedTransaction) { - throw new Error( - "Unsigned transaction is missing from deployEscrow response.", - ); - } - - /** - * @Note: - * - We need to sign the transaction using your private key - * - The result will be a signed transaction - */ - const signedXdr = await signTransaction({ - unsignedTransaction, - address: walletAddress || "", - }); - - if (!signedXdr) { - throw new Error("Signed transaction is missing."); - } - - /** - * @Note: - * - We need to send the signed transaction to the API - * - The data will be an SendTransactionResponse - */ - const data = await sendTransaction({ - signedXdr, - returnEscrowDataIsRequired: true, - }); - - /** - * @Responses: - * data.status === "SUCCESS" - * - Escrow created successfully - * - Set the escrow in the context - * - Set the active tab to "escrow" - * - Show a success toast - * - * data.status == "ERROR" - * - Show an error toast - */ - if (data && data.status === "SUCCESS") { - const escrow = buildEscrowFromResponse( - data as InitializeEscrowResponse, - walletAddress || "", - ); - setEscrow(escrow); - setActiveTab("escrow"); - toast.success("Escrow Created"); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - const nextStep = async () => { - const fields = getStepFields(currentStep); - const isValid = await form.trigger(fields); - - if (isValid) { - setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1)); - } - }; - - const prevStep = () => { - setCurrentStep((prev) => Math.max(prev - 1, 0)); - }; - - const getStepFields = ( - step: number, - ): (keyof z.infer)[] => { - switch (step) { - case 0: - return ["title", "engagementId", "description"]; - case 1: - return ["amount", "platformFee", "trustline", "receiverMemo"]; - case 2: - return ["roles"]; - case 3: - return ["milestones"]; - default: - return []; - } - }; - - return { - form, - loading, - response, - trustlinesOptions, - currentStep, - addMilestone, - removeMilestone, - loadTemplate, - onSubmit, - nextStep, - prevStep, - }; -}; diff --git a/src/components/modules/escrows/hooks/release-funds-form.hook.ts b/src/components/modules/escrows/hooks/release-funds-form.hook.ts deleted file mode 100644 index 78d89664..00000000 --- a/src/components/modules/escrows/hooks/release-funds-form.hook.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { useEscrowContext } from "@/providers/escrow.provider"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; -import { formSchema } from "../schemas/release-funds-form.schema"; -import { toast } from "sonner"; -import { signTransaction } from "../../auth/helpers/stellar-wallet-kit.helper"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { WalletError } from "@/@types/errors.entity"; -import { - Escrow, - EscrowRequestResponse, - ReleaseFundsPayload, -} from "@trustless-work/escrow/types"; -import { - useReleaseFunds, - useSendTransaction, -} from "@trustless-work/escrow/hooks"; - -export const useReleaseFundsForm = () => { - const { escrow } = useEscrowContext(); - const { setEscrow } = useEscrowContext(); - const { walletAddress } = useWalletContext(); - const [loading, setLoading] = useState(false); - const [response, setResponse] = useState(null); - const { releaseFunds } = useReleaseFunds(); - const { sendTransaction } = useSendTransaction(); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - contractId: escrow?.contractId || "", - releaseSigner: escrow?.roles.releaseSigner || "", - signer: walletAddress || "Connect your wallet to get your address", - }, - }); - - const onSubmit = async (payload: ReleaseFundsPayload) => { - setLoading(true); - setResponse(null); - - try { - /** - * API call by using the trustless work hooks - * @Note: - * - We need to pass the payload to the releaseFunds function - * - The result will be an unsigned transaction - */ - const { unsignedTransaction } = await releaseFunds(payload); - - if (!unsignedTransaction) { - throw new Error( - "Unsigned transaction is missing from releaseFunds response.", - ); - } - - /** - * @Note: - * - We need to sign the transaction using your private key - * - The result will be a signed transaction - */ - const signedXdr = await signTransaction({ - unsignedTransaction, - address: walletAddress || "", - }); - - if (!signedXdr) { - throw new Error("Signed transaction is missing."); - } - - /** - * @Note: - * - We need to send the signed transaction to the API - * - The data will be an SendTransactionResponse - */ - const data = await sendTransaction({ - signedXdr, - returnEscrowDataIsRequired: false, - }); - - /** - * @Responses: - * data.status === "SUCCESS" - * - Escrow updated successfully - * - Set the escrow in the context - * - Show a success toast - * - * data.status == "ERROR" - * - Show an error toast - */ - if (data.status === "SUCCESS" && escrow) { - const escrowUpdated: Escrow = { - ...escrow, - flags: { - releaseFlag: true, - }, - balance: "0", - }; - - setEscrow(escrowUpdated); - - toast.success("The escrow has been released"); - setResponse(data); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - return { form, loading, response, onSubmit }; -}; diff --git a/src/components/modules/escrows/hooks/resolve-dispute-form.hook.ts b/src/components/modules/escrows/hooks/resolve-dispute-form.hook.ts deleted file mode 100644 index 407c3ca2..00000000 --- a/src/components/modules/escrows/hooks/resolve-dispute-form.hook.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; -import { useEscrowContext } from "@/providers/escrow.provider"; -import { formSchema } from "../schemas/resolve-dispute-form.schema"; -import { toast } from "sonner"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { signTransaction } from "../../auth/helpers/stellar-wallet-kit.helper"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { WalletError } from "@/@types/errors.entity"; -import { - Escrow, - EscrowRequestResponse, - ResolveDisputePayload, -} from "@trustless-work/escrow/types"; -import { - useResolveDispute, - useSendTransaction, -} from "@trustless-work/escrow/hooks"; - -export const useResolveDisputeForm = () => { - const { escrow } = useEscrowContext(); - const { setEscrow } = useEscrowContext(); - const [loading, setLoading] = useState(false); - const [response, setResponse] = useState(null); - const { walletAddress } = useWalletContext(); - const { resolveDispute } = useResolveDispute(); - const { sendTransaction } = useSendTransaction(); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - contractId: escrow?.contractId || "", - disputeResolver: escrow?.roles.disputeResolver || "", - approverFunds: "0", - receiverFunds: "0", - }, - }); - - const onSubmit = async (payload: ResolveDisputePayload) => { - setLoading(true); - setResponse(null); - - try { - /** - * API call by using the trustless work hooks - * @Note: - * - We need to pass the payload to the resolveDispute function - * - The result will be an unsigned transaction - */ - const { unsignedTransaction } = await resolveDispute(payload); - - if (!unsignedTransaction) { - throw new Error( - "Unsigned transaction is missing from resolveDispute response.", - ); - } - - /** - * @Note: - * - We need to sign the transaction using your private key - * - The result will be a signed transaction - */ - const signedXdr = await signTransaction({ - unsignedTransaction, - address: walletAddress || "", - }); - - if (!signedXdr) { - throw new Error("Signed transaction is missing."); - } - - /** - * @Note: - * - We need to send the signed transaction to the API - * - The data will be an SendTransactionResponse - */ - const data = await sendTransaction({ - signedXdr, - returnEscrowDataIsRequired: false, - }); - - /** - * @Responses: - * data.status === "SUCCESS" - * - Escrow updated successfully - * - Set the escrow in the context - * - Show a success toast - * - * data.status == "ERROR" - * - Show an error toast - */ - if (data.status === "SUCCESS" && escrow) { - const escrowUpdated: Escrow = { - ...escrow, - flags: { - resolvedFlag: true, - }, - balance: ( - Number(escrow?.balance) - - Number(payload.approverFunds) - - Number(payload.receiverFunds) - ).toString(), - }; - - setEscrow(escrowUpdated); - - toast.success("Dispute Resolved"); - setResponse(data); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - return { form, loading, response, onSubmit }; -}; diff --git a/src/components/modules/escrows/hooks/start-dispute-form.hook.ts b/src/components/modules/escrows/hooks/start-dispute-form.hook.ts deleted file mode 100644 index 4f366cd4..00000000 --- a/src/components/modules/escrows/hooks/start-dispute-form.hook.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { useEscrowContext } from "@/providers/escrow.provider"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; -import { formSchema } from "../schemas/start-dispute-form.schema"; -import { toast } from "sonner"; -import { signTransaction } from "../../auth/helpers/stellar-wallet-kit.helper"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { WalletError } from "@/@types/errors.entity"; -import { - Escrow, - EscrowRequestResponse, - StartDisputePayload, -} from "@trustless-work/escrow/types"; -import { - useSendTransaction, - useStartDispute, -} from "@trustless-work/escrow/hooks"; - -export const useStartDisputeForm = () => { - const { escrow } = useEscrowContext(); - const { setEscrow } = useEscrowContext(); - const { walletAddress } = useWalletContext(); - const [loading, setLoading] = useState(false); - const [response, setResponse] = useState(null); - const { startDispute } = useStartDispute(); - const { sendTransaction } = useSendTransaction(); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - contractId: escrow?.contractId || "", - signer: walletAddress || "Connect your wallet to get your address", - }, - }); - - const onSubmit = async (payload: StartDisputePayload) => { - setLoading(true); - setResponse(null); - - try { - /** - * API call by using the trustless work hooks - * @Note: - * - We need to pass the payload to the startDispute function - * - The result will be an unsigned transaction - */ - const { unsignedTransaction } = await startDispute(payload); - - if (!unsignedTransaction) { - throw new Error( - "Unsigned transaction is missing from startDispute response.", - ); - } - - /** - * @Note: - * - We need to sign the transaction using your private key - * - The result will be a signed transaction - */ - const signedXdr = await signTransaction({ - unsignedTransaction, - address: walletAddress || "", - }); - - if (!signedXdr) { - throw new Error("Signed transaction is missing."); - } - - /** - * @Note: - * - We need to send the signed transaction to the API - * - The data will be an SendTransactionResponse - */ - const data = await sendTransaction({ - signedXdr, - returnEscrowDataIsRequired: false, - }); - - /** - * @Responses: - * data.status === "SUCCESS" - * - Escrow updated successfully - * - Set the escrow in the context - * - Show a success toast - * - * data.status == "ERROR" - * - Show an error toast - */ - if (data.status === "SUCCESS" && escrow) { - const escrowUpdated: Escrow = { - ...escrow, - flags: { - disputeFlag: true, - }, - }; - - setEscrow(escrowUpdated); - - toast.success("Dispute Started"); - setResponse(data); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - return { form, loading, response, onSubmit }; -}; diff --git a/src/components/modules/escrows/hooks/update-escrow-form.hook.ts b/src/components/modules/escrows/hooks/update-escrow-form.hook.ts deleted file mode 100644 index f4e51f6d..00000000 --- a/src/components/modules/escrows/hooks/update-escrow-form.hook.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import { useForm, useFieldArray } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; -import { useEscrowContext } from "@/providers/escrow.provider"; -import { useWalletContext } from "@/providers/wallet.provider"; -import { useState } from "react"; -import { toast } from "sonner"; -import { formSchema } from "../schemas/update-escrow-form.schema"; -import { handleError } from "@/errors/utils/handle-errors"; -import { AxiosError } from "axios"; -import { WalletError } from "@/@types/errors.entity"; -import { signTransaction } from "../../auth/helpers/stellar-wallet-kit.helper"; -import { - Escrow, - UpdateEscrowPayload, - UpdateEscrowResponse, -} from "@trustless-work/escrow/types"; -import { - useSendTransaction, - useUpdateEscrow, -} from "@trustless-work/escrow/hooks"; - -export const useUpdateEscrowForm = () => { - const { escrow } = useEscrowContext(); - const { walletAddress } = useWalletContext(); - const { setEscrow } = useEscrowContext(); - const [response, setResponse] = useState(null); - const [loading, setLoading] = useState(false); - const { updateEscrow } = useUpdateEscrow(); - const { sendTransaction } = useSendTransaction(); - - const form = useForm>({ - resolver: zodResolver(formSchema) as any, - defaultValues: { - signer: walletAddress || "", - contractId: escrow?.contractId || "", - escrow: { - title: escrow?.title || "", - engagementId: escrow?.engagementId || "", - description: escrow?.description || "", - amount: escrow?.amount.toString() || "", - platformFee: (Number(escrow?.platformFee) / 100).toString() || "", - receiverMemo: escrow?.receiverMemo || 0, - roles: { - approver: escrow?.roles.approver || "", - serviceProvider: escrow?.roles.serviceProvider || "", - platformAddress: escrow?.roles.platformAddress || "", - releaseSigner: escrow?.roles.releaseSigner || "", - disputeResolver: escrow?.roles.disputeResolver || "", - receiver: escrow?.roles.receiver || "", - }, - trustline: { - address: escrow?.trustline.address || "", - decimals: escrow?.trustline.decimals || 10000000, - }, - milestones: escrow?.milestones || [ - { - description: "", - status: "pending", - evidence: "", - approvedFlag: false, - }, - ], - }, - }, - }); - - const { fields, append, remove } = useFieldArray({ - control: form.control, - name: "escrow.milestones", - }); - - const onSubmit = async (payload: UpdateEscrowPayload) => { - setLoading(true); - setResponse(null); - - try { - /** - * API call by using the trustless work hooks - * @Note: - * - We need to pass the payload to the updateEscrow function - * - The result will be an unsigned transaction - */ - const { unsignedTransaction } = await updateEscrow(payload); - - if (!unsignedTransaction) { - throw new Error( - "Unsigned transaction is missing from updateEscrow response.", - ); - } - - /** - * @Note: - * - We need to sign the transaction using your private key - * - The result will be a signed transaction - */ - const signedXdr = await signTransaction({ - unsignedTransaction, - address: walletAddress || "", - }); - - if (!signedXdr) { - throw new Error("Signed transaction is missing."); - } - - /** - * @Note: - * - We need to send the signed transaction to the API - * - The data will be an SendTransactionResponse - */ - const data = await sendTransaction({ - signedXdr, - returnEscrowDataIsRequired: true, - }); - - /** - * @Responses: - * data.status === "SUCCESS" - * - Escrow updated successfully - * - Set the escrow in the context - * - Show a success toast - * - * data.status == "ERROR" - * - Show an error toast - */ - if (data.status === "SUCCESS" && escrow) { - const escrowUpdated: Escrow = { - ...escrow, - ...payload.escrow, - signer: payload.signer, - contractId: payload.contractId, - }; - - setEscrow(escrowUpdated); - setResponse(data as UpdateEscrowResponse); - toast.success("Escrow Updated"); - } - } catch (error: unknown) { - const mappedError = handleError(error as AxiosError | WalletError); - console.error("Error:", mappedError.message); - - toast.error( - mappedError ? mappedError.message : "An unknown error occurred", - ); - } finally { - setLoading(false); - } - }; - - return { form, loading, response, fields, append, remove, onSubmit }; -}; diff --git a/src/components/modules/escrows/schemas/change-milestone-flag-form.schema.ts b/src/components/modules/escrows/schemas/change-milestone-flag-form.schema.ts deleted file mode 100644 index a6b7fe2a..00000000 --- a/src/components/modules/escrows/schemas/change-milestone-flag-form.schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { isValidWallet } from "@/helpers/is-valid-wallet.helper"; -import { z } from "zod"; - -export const formSchema = z.object({ - contractId: z.string().min(1, "Contract ID is required"), - milestoneIndex: z.string().min(1, "Milestone index is required"), - newFlag: z.boolean(), - approver: z - .string() - .min(1, { - message: "Approver is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Approver must be a valid wallet.", - }), -}); diff --git a/src/components/modules/escrows/schemas/change-milestone-status-form.schema.ts b/src/components/modules/escrows/schemas/change-milestone-status-form.schema.ts deleted file mode 100644 index 3d764ab6..00000000 --- a/src/components/modules/escrows/schemas/change-milestone-status-form.schema.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { isValidWallet } from "@/helpers/is-valid-wallet.helper"; -import { z } from "zod"; - -export const formSchema = z.object({ - contractId: z.string().min(1, "Contract ID is required"), - milestoneIndex: z.string().min(1, "Milestone index is required"), - newStatus: z.string().min(1, "New status is required"), - serviceProvider: z - .string() - .min(1, { - message: "Service provider is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Service provider must be a valid wallet.", - }), - evidence: z.string().optional(), -}); diff --git a/src/components/modules/escrows/schemas/fund-escrow-form.schema.ts b/src/components/modules/escrows/schemas/fund-escrow-form.schema.ts deleted file mode 100644 index f89ec64d..00000000 --- a/src/components/modules/escrows/schemas/fund-escrow-form.schema.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { z } from "zod"; - -export const formSchema = z.object({ - contractId: z.string().min(1, "Contract ID is required"), - signer: z.string().min(1, "Signer address is required"), - amount: z.string().min(1, { - message: "Amount is required.", - }), -}); diff --git a/src/components/modules/escrows/schemas/get-escrow-form.schema.ts b/src/components/modules/escrows/schemas/get-escrow-form.schema.ts deleted file mode 100644 index b05fc37c..00000000 --- a/src/components/modules/escrows/schemas/get-escrow-form.schema.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { z } from "zod"; - -export const formSchema = z.object({ - contractId: z.string().min(1, "Contract ID is required"), - signer: z.string().min(1, "Signer Address is required"), -}); diff --git a/src/components/modules/escrows/schemas/get-multiple-escrow-balances-form.schema.ts b/src/components/modules/escrows/schemas/get-multiple-escrow-balances-form.schema.ts deleted file mode 100644 index fdeff3a8..00000000 --- a/src/components/modules/escrows/schemas/get-multiple-escrow-balances-form.schema.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { z } from "zod"; - -export const formSchema = z.object({ - signer: z.string().min(1, "Signer address is required"), - addresses: z - .array( - z.object({ - value: z.string().min(1, "Address is required"), - }), - ) - .min(1, "At least one address is required"), -}); diff --git a/src/components/modules/escrows/schemas/initialize-escrow-form.schema.ts b/src/components/modules/escrows/schemas/initialize-escrow-form.schema.ts deleted file mode 100644 index 1cae0501..00000000 --- a/src/components/modules/escrows/schemas/initialize-escrow-form.schema.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { isValidWallet } from "@/helpers/is-valid-wallet.helper"; -import { z } from "zod"; - -export const formSchema = z.object({ - signer: z.string().min(1, { - message: "Signer is required.", - }), - engagementId: z.string().min(1, { - message: "Engagement is required.", - }), - title: z.string().min(1, { - message: "Title is required.", - }), - description: z.string().min(10, { - message: "Description must be at least 10 characters long.", - }), - amount: z.string().min(1, { - message: "Amount is required.", - }), - platformFee: z.string().min(1, { - message: "Platform fee is required.", - }), - receiverMemo: z.number().min(0, { - message: "Receiver memo must be a non-negative number.", - }), - roles: z.object({ - approver: z - .string() - .min(1, { - message: "Approver is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Approver must be a valid wallet.", - }), - serviceProvider: z - .string() - .min(1, { - message: "Service provider is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Service provider must be a valid wallet.", - }), - platformAddress: z - .string() - .min(1, { - message: "Platform address is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Platform address must be a valid wallet.", - }), - releaseSigner: z - .string() - .min(1, { - message: "Release signer is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Release signer must be a valid wallet.", - }), - disputeResolver: z - .string() - .min(1, { - message: "Dispute resolver is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Dispute resolver must be a valid wallet.", - }), - receiver: z - .string() - .min(1, { - message: "Receiver address is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Receiver address must be a valid wallet.", - }), - }), - trustline: z.object({ - address: z.string().min(1, { - message: "Trustline address is required.", - }), - decimals: z.number().default(10000000), - }), - milestones: z - .array( - z.object({ - description: z.string().min(1, { - message: "Milestone description is required.", - }), - status: z.string().default("pending"), - evidence: z.string().default(""), - approvedFlag: z.boolean().default(false), - }), - ) - .min(1, { message: "At least one milestone is required." }), -}); diff --git a/src/components/modules/escrows/schemas/release-funds-form.schema.ts b/src/components/modules/escrows/schemas/release-funds-form.schema.ts deleted file mode 100644 index 5f05404e..00000000 --- a/src/components/modules/escrows/schemas/release-funds-form.schema.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { isValidWallet } from "@/helpers/is-valid-wallet.helper"; -import { z } from "zod"; - -export const formSchema = z.object({ - contractId: z.string().min(1, "Contract ID is required"), - releaseSigner: z - .string() - .min(1, { - message: "Release signer is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Release signer must be a valid wallet.", - }), - signer: z.string().min(1, "Signer address is required"), -}); diff --git a/src/components/modules/escrows/schemas/resolve-dispute-form.schema.ts b/src/components/modules/escrows/schemas/resolve-dispute-form.schema.ts deleted file mode 100644 index 6bee51d6..00000000 --- a/src/components/modules/escrows/schemas/resolve-dispute-form.schema.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { isValidWallet } from "@/helpers/is-valid-wallet.helper"; -import { z } from "zod"; - -export const formSchema = z.object({ - contractId: z.string().min(1, "Contract ID is required"), - disputeResolver: z - .string() - .min(1, { - message: "Dispute resolver is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Dispute resolver must be a valid wallet.", - }), - approverFunds: z.string().min(1, "Approver funds is required"), - receiverFunds: z.string().min(1, "Receiver funds is required"), -}); diff --git a/src/components/modules/escrows/schemas/start-dispute-form.schema.ts b/src/components/modules/escrows/schemas/start-dispute-form.schema.ts deleted file mode 100644 index d2731497..00000000 --- a/src/components/modules/escrows/schemas/start-dispute-form.schema.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { z } from "zod"; - -export const formSchema = z.object({ - contractId: z.string().min(1, "Contract ID is required"), - signer: z.string().min(1, "Signer address is required"), -}); diff --git a/src/components/modules/escrows/schemas/update-escrow-form.schema.ts b/src/components/modules/escrows/schemas/update-escrow-form.schema.ts deleted file mode 100644 index 3de04c66..00000000 --- a/src/components/modules/escrows/schemas/update-escrow-form.schema.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { isValidWallet } from "@/helpers/is-valid-wallet.helper"; -import { z } from "zod"; - -export const formSchema = z.object({ - contractId: z.string().min(1, { - message: "Contract ID is required.", - }), - signer: z.string().min(1, { - message: "Signer is required.", - }), - escrow: z.object({ - title: z.string().min(1, { - message: "Title is required.", - }), - engagementId: z.string().min(1, { - message: "Engagement is required.", - }), - description: z.string().min(10, { - message: "Description must be at least 10 characters long.", - }), - amount: z.string().min(1, { - message: "Amount is required.", - }), - platformFee: z.string().min(1, { - message: "Platform fee is required.", - }), - receiverMemo: z.number().min(0, { - message: "Receiver memo must be a non-negative number.", - }), - roles: z.object({ - approver: z - .string() - .min(1, { - message: "Approver is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Approver must be a valid wallet.", - }), - serviceProvider: z - .string() - .min(1, { - message: "Service provider is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Service provider must be a valid wallet.", - }), - platformAddress: z - .string() - .min(1, { - message: "Platform address is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Platform address must be a valid wallet.", - }), - releaseSigner: z - .string() - .min(1, { - message: "Release signer is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Release signer must be a valid wallet.", - }), - disputeResolver: z - .string() - .min(1, { - message: "Dispute resolver is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Dispute resolver must be a valid wallet.", - }), - receiver: z - .string() - .min(1, { - message: "Receiver address is required.", - }) - .refine((value) => isValidWallet(value), { - message: "Receiver address must be a valid wallet.", - }), - }), - trustline: z.object({ - address: z.string().min(1, { - message: "Trustline address is required.", - }), - decimals: z.number().default(10000000), - }), - milestones: z - .array( - z.object({ - description: z.string().min(1, { - message: "Milestone description is required.", - }), - status: z.string().default("pending"), - evidence: z.string().default(""), - approvedFlag: z.boolean().default(false), - }), - ) - .min(1, { message: "At least one milestone is required." }), - }), -}); diff --git a/src/components/modules/escrows/ui/ConnectWalletWarning.tsx b/src/components/modules/escrows/ui/ConnectWalletWarning.tsx deleted file mode 100644 index 64b0c91f..00000000 --- a/src/components/modules/escrows/ui/ConnectWalletWarning.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { AlertCircle, Wallet } from "lucide-react"; -import { useWallet } from "@/components/modules/auth/hooks/wallet.hook"; - -export const ConnectWalletWarning = () => { - const { handleConnect } = useWallet(); - - return ( -
-
- -
-

Wallet Connection Required

-

- To access and interact with the Trustless Work API endpoints, you need - to connect your Stellar wallet first. -

-
- -
-
- -

Your wallet information is never stored on our servers

-
-
- ); -}; diff --git a/src/components/modules/escrows/ui/cards/EntityCard.tsx b/src/components/modules/escrows/ui/cards/EntityCard.tsx deleted file mode 100644 index 6663a840..00000000 --- a/src/components/modules/escrows/ui/cards/EntityCard.tsx +++ /dev/null @@ -1,20 +0,0 @@ -interface EntityCardProps { - name: string; - entity: string; - icon: React.ReactNode; -} - -export const EntityCard = ({ name, entity, icon }: EntityCardProps) => { - return ( -
-
- {icon} -
- -
-

{name}

-

{entity}

-
-
- ); -}; diff --git a/src/components/modules/escrows/ui/endpoints/DeployEndpoints.tsx b/src/components/modules/escrows/ui/endpoints/DeployEndpoints.tsx deleted file mode 100644 index 97ad4a34..00000000 --- a/src/components/modules/escrows/ui/endpoints/DeployEndpoints.tsx +++ /dev/null @@ -1,71 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { InitializeEscrowForm } from "../forms/InitializeEscrowForm"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { useInitializeEscrow } from "../../hooks/initialize-escrow-form.hook"; - -export function DeployEndpoints() { - const { - form, - loading, - response, - trustlinesOptions, - currentStep, - addMilestone, - removeMilestone, - loadTemplate, - onSubmit, - nextStep, - prevStep, - } = useInitializeEscrow(); - - const handleLoadTemplate = () => { - loadTemplate(); - }; - - return ( - - -
- Deploy Endpoints - - Deploy and initialize escrow contracts on the Stellar blockchain - -
- - -
- - ({ - value: option.value, - label: option.label || option.value, - }))} - currentStep={currentStep} - nextStep={nextStep} - prevStep={prevStep} - /> - -
- ); -} diff --git a/src/components/modules/escrows/ui/endpoints/EscrowEndpoints.tsx b/src/components/modules/escrows/ui/endpoints/EscrowEndpoints.tsx deleted file mode 100644 index f1377217..00000000 --- a/src/components/modules/escrows/ui/endpoints/EscrowEndpoints.tsx +++ /dev/null @@ -1,123 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { StartDisputeForm } from "../forms/StartDisputeForm"; -import { GetEscrowForm } from "../forms/GetEscrowForm"; -import { FundEscrowForm } from "../forms/FundEscrowForm"; -import { ChangeMilestoneStatusForm } from "../forms/ChangeMilestoneStatusForm"; -import { ChangeMilestoneFlagForm } from "../forms/ChangeMilestoneFlagForm"; -import { ReleaseFundsForm } from "../forms/ReleaseFundsForm"; -import { ResolveDisputeForm } from "../forms/ResolveDisputeForm"; -import { UpdateEscrowForm } from "../forms/UpdateEscrowForm"; -import { EscrowCreatedSection } from "../sections/EscrowCreatedSection"; -import { useEscrowContext } from "@/providers/escrow.provider"; -import { useTabsContext } from "@/providers/tabs.provider"; -import { Button } from "@/components/ui/button"; - -export function EscrowEndpoints() { - const [activeTabEscrow, setActiveTabEscrow] = useState("get-escrow"); - const { resetEscrow } = useEscrowContext(); - const { setActiveTab } = useTabsContext(); - const { escrow } = useEscrowContext(); - - return ( - - -
- Escrow Endpoints - - Manage escrow contracts, milestones, and funds - -
- - {escrow && ( - - )} -
- - - - - Get Escrow - - - Fund Escrow - - - Change Status - - - Approve Milestone - - - Start Dispute - - - Resolve Dispute - - - Release Funds - - - Update Escrow - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -
-
-
-
- ); -} diff --git a/src/components/modules/escrows/ui/endpoints/HelperEndpoints.tsx b/src/components/modules/escrows/ui/endpoints/HelperEndpoints.tsx deleted file mode 100644 index 98c21247..00000000 --- a/src/components/modules/escrows/ui/endpoints/HelperEndpoints.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { GetMultipleEscrowBalanceForm } from "../forms/GetMultipleEscrowBalanceForm"; - -export function HelperEndpoints() { - const [activeTab, setActiveTab] = useState("get-multiple-escrow-balance"); - - return ( - - - Helper Endpoints - - Utility endpoints for blockchain interactions - - - - - - - Get Balances - - -
- - - -
-
-
-
- ); -} diff --git a/src/components/modules/escrows/ui/forms/ChangeMilestoneFlagForm.tsx b/src/components/modules/escrows/ui/forms/ChangeMilestoneFlagForm.tsx deleted file mode 100644 index 33428504..00000000 --- a/src/components/modules/escrows/ui/forms/ChangeMilestoneFlagForm.tsx +++ /dev/null @@ -1,127 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Switch } from "@/components/ui/switch"; -import { - Form, - FormField, - FormItem, - FormLabel, - FormControl, - FormMessage, -} from "@/components/ui/form"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { useChangeMilestoneFlagForm } from "../../hooks/change-milestone-flag-form.hook"; -import { useEscrowContext } from "@/providers/escrow.provider"; -import { ResponseDisplay } from "@/utils/response-display"; - -export function ChangeMilestoneFlagForm() { - const { form, milestones, loading, response, onSubmit } = - useChangeMilestoneFlagForm(); - const { escrow } = useEscrowContext(); - - return ( -
-
- - ( - - Contract / Escrow ID - - - - - - )} - /> - - ( - - Approver Address - - - - - - )} - /> - - ( - - Milestone Index - - - - - - )} - /> - - ( - -
- Approve Milestone -
- - - - -
- )} - /> - - - - - - -
- ); -} diff --git a/src/components/modules/escrows/ui/forms/ChangeMilestoneStatusForm.tsx b/src/components/modules/escrows/ui/forms/ChangeMilestoneStatusForm.tsx deleted file mode 100644 index 20cefc07..00000000 --- a/src/components/modules/escrows/ui/forms/ChangeMilestoneStatusForm.tsx +++ /dev/null @@ -1,137 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { - Form, - FormField, - FormItem, - FormLabel, - FormControl, - FormMessage, -} from "@/components/ui/form"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { useChangeMilestoneStatusForm } from "../../hooks/change-milestone-status-form.hook"; -import { useEscrowContext } from "@/providers/escrow.provider"; -import { ResponseDisplay } from "@/utils/response-display"; - -export function ChangeMilestoneStatusForm() { - const { form, milestones, loading, response, onSubmit } = - useChangeMilestoneStatusForm(); - const { escrow } = useEscrowContext(); - - return ( -
-
- - ( - - Contract / Escrow ID - - - - - - )} - /> - - ( - - Service Provider Address - - - - - - )} - /> - - ( - - Milestone Index - - - - - - )} - /> - - ( - - New Status - - - - - - )} - /> - - ( - - Evidence (optional) - - - - - - )} - /> - - - - - - -
- ); -} diff --git a/src/components/modules/escrows/ui/forms/FundEscrowForm.tsx b/src/components/modules/escrows/ui/forms/FundEscrowForm.tsx deleted file mode 100644 index e6313f82..00000000 --- a/src/components/modules/escrows/ui/forms/FundEscrowForm.tsx +++ /dev/null @@ -1,85 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { - Form, - FormField, - FormItem, - FormLabel, - FormControl, - FormMessage, -} from "@/components/ui/form"; - -import { useFundEscrowForm } from "../../hooks/fund-escrow-form.hook"; -import { useEscrowContext } from "@/providers/escrow.provider"; -import { ResponseDisplay } from "@/utils/response-display"; - -export function FundEscrowForm() { - const { form, loading, response, onSubmit } = useFundEscrowForm(); - const { escrow } = useEscrowContext(); - - return ( -
-
- - ( - - Contract / Escrow ID - - - - - - )} - /> - - ( - - Signer Address - - - - - - )} - /> - - ( - - Amount - - = Number(escrow?.amount)} - type="text" - {...field} - /> - - - - )} - /> - - - - - - -
- ); -} diff --git a/src/components/modules/escrows/ui/forms/GetEscrowForm.tsx b/src/components/modules/escrows/ui/forms/GetEscrowForm.tsx deleted file mode 100644 index 33d53502..00000000 --- a/src/components/modules/escrows/ui/forms/GetEscrowForm.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { - Form, - FormField, - FormItem, - FormLabel, - FormControl, - FormMessage, -} from "@/components/ui/form"; -import { useGetEscrowForm } from "../../hooks/get-escrow-form.hook"; -import { ResponseDisplay } from "@/utils/response-display"; - -export function GetEscrowForm() { - const { form, loading, response, onSubmit } = useGetEscrowForm(); - - return ( -
- - ( - - Contract / Escrow ID - - - - - - )} - /> - - ( - - Signer Address - - - - - - )} - /> - - - - - - - ); -} diff --git a/src/components/modules/escrows/ui/forms/GetMultipleEscrowBalanceForm.tsx b/src/components/modules/escrows/ui/forms/GetMultipleEscrowBalanceForm.tsx deleted file mode 100644 index 1d9dcfa2..00000000 --- a/src/components/modules/escrows/ui/forms/GetMultipleEscrowBalanceForm.tsx +++ /dev/null @@ -1,95 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Plus, Trash } from "lucide-react"; -import { - Form, - FormField, - FormItem, - FormLabel, - FormControl, - FormMessage, -} from "@/components/ui/form"; -import { useGetMultipleEscrowBalancesForm } from "../../hooks/get-multiple-escrow-balances-form.hook"; -import { ResponseDisplay } from "@/utils/response-display"; - -export function GetMultipleEscrowBalanceForm() { - const { form, loading, response, fields, append, remove, onSubmit } = - useGetMultipleEscrowBalancesForm(); - - return ( -
-
- - {/* Signer Address */} - ( - - Signer Address - - - - - - )} - /> - - {/* Contract Addresses */} -
-
- - -
- - {fields.map((field, index) => ( - ( - - - - - {fields.length > 1 && ( - - )} - - - )} - /> - ))} -
- - - - - - -
- ); -} diff --git a/src/components/modules/escrows/ui/forms/InitializeEscrowForm.tsx b/src/components/modules/escrows/ui/forms/InitializeEscrowForm.tsx deleted file mode 100644 index 97ec2318..00000000 --- a/src/components/modules/escrows/ui/forms/InitializeEscrowForm.tsx +++ /dev/null @@ -1,448 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; -import { Plus, Trash, ChevronLeft, ChevronRight } from "lucide-react"; -import { - Form, - FormField, - FormItem, - FormLabel, - FormControl, - FormMessage, - FormDescription, -} from "@/components/ui/form"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { UseFormReturn } from "react-hook-form"; -import { z } from "zod"; -import { formSchema } from "../../schemas/initialize-escrow-form.schema"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { cn } from "@/lib/utils"; -import { steps } from "../../constants/initialize-steps.constant"; -import { InitializeEscrowResponse } from "@trustless-work/escrow/types"; -import { ResponseDisplay } from "@/utils/response-display"; - -interface InitializeEscrowFormProps { - form: UseFormReturn>; - loading?: boolean; - response: InitializeEscrowResponse | null; - trustlinesOptions: { value: string; label: string }[]; - currentStep: number; - onSubmit: (data: z.infer) => Promise; - addMilestone: () => void; - removeMilestone: (index: number) => void; - nextStep: () => void; - prevStep: () => void; -} - -export const InitializeEscrowForm = ({ - form, - loading, - response, - trustlinesOptions, - currentStep, - onSubmit, - addMilestone, - removeMilestone, - nextStep, - prevStep, -}: InitializeEscrowFormProps) => { - const renderStep = () => { - const currentStepData = steps[currentStep]; - - return ( - - - - {currentStepData.title} - - {currentStepData.description} - - -
- {currentStep === 0 && ( - <> - ( - - Signer Address - - - - - - )} - /> - - ( - - Title - - - - - - )} - /> - - ( - - Engagement ID - - - - - - )} - /> - - ( - - Description - -