Skip to content

Commit a7cc570

Browse files
authored
Merge pull request #296 from mubking/Add-fractionalized
feat: add fractionalized RWA purchasing component (#192)
2 parents 9c03cf7 + 15eca45 commit a7cc570

2 files changed

Lines changed: 350 additions & 25 deletions

File tree

src/app/marketplace/inv-123/page.tsx

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,76 @@
11
"use client";
22

3-
import React from "react";
3+
import React, { useState, useEffect } from "react";
4+
import { Server } from "soroban-client";
45
import StickyHeader from "../../../components/StickyHeader";
6+
import FractionalPurchaseModal, {
7+
type Invoice,
8+
} from "../../../components/FractionalPurchaseModal";
9+
import { useTokenStore } from "../../../stores/tokenStore";
510
import { ArrowLeft, ExternalLink, Shield, TrendingUp } from "lucide-react";
611
import Link from "next/link";
7-
import { useInvoice } from "@/hooks/useInvoice";
812

9-
const INVOICE_ID = "INV-00123";
13+
// Stellar testnet USDC issuer
14+
const USDC_CODE = "USDC";
15+
const USDC_ISSUER = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5";
16+
17+
const server = new Server("https://soroban-testnet.stellar.org", {
18+
allowHttp: true,
19+
});
20+
21+
// TODO: Replace with real invoice data from your API / contract query
22+
const INVOICE_DATA: Invoice = {
23+
id: "INV-00123",
24+
faceValue: 50000,
25+
apy: 8.5,
26+
dueDate: "2026-12-15",
27+
currency: "USDC",
28+
};
1029

1130
export default function InvoiceDetailPage() {
12-
const { invoice, loading, error } = useInvoice(INVOICE_ID);
31+
const { publicKey, isConnected } = useTokenStore();
32+
const [showBuyModal, setShowBuyModal] = useState(false);
33+
const [usdcBalance, setUsdcBalance] = useState("0");
34+
35+
// Fetch live USDC balance from Stellar network whenever wallet connects
36+
useEffect(() => {
37+
if (!isConnected || !publicKey) {
38+
setUsdcBalance("0");
39+
return;
40+
}
41+
42+
const fetchBalance = async () => {
43+
try {
44+
const accountResponse = await server.accounts().accountId(publicKey).call();
45+
const usdcEntry = accountResponse.balances.find(
46+
(b: any) =>
47+
b.asset_code === USDC_CODE && b.asset_issuer === USDC_ISSUER
48+
);
49+
setUsdcBalance(usdcEntry ? usdcEntry.balance : "0");
50+
} catch {
51+
setUsdcBalance("0");
52+
}
53+
};
54+
55+
fetchBalance();
56+
}, [isConnected, publicKey]);
57+
58+
const handleBuyFraction = async (
59+
amountStroops: string,
60+
invoiceId: string
61+
) => {
62+
// TODO: Replace with your real Soroban client call, e.g.:
63+
// await sorobanClient.buy_fraction({
64+
// invoice_id: invoiceId,
65+
// amount: BigInt(amountStroops),
66+
// });
67+
console.log("buy_fraction called:", { invoiceId, amountStroops });
68+
await new Promise((r) => setTimeout(r, 1500));
69+
};
1370

1471
return (
1572
<div className="min-h-screen bg-tradeflow-dark text-white font-sans">
73+
{/* Sticky Header */}
1674
<StickyHeader
1775
title="INV-123"
1876
subtitle="Real World Asset token details and performance metrics"
@@ -25,7 +83,10 @@ export default function InvoiceDetailPage() {
2583
<ArrowLeft size={16} />
2684
Back
2785
</Link>
28-
<button className="flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors">
86+
<button
87+
onClick={() => setShowBuyModal(true)}
88+
className="flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors"
89+
>
2990
<ExternalLink size={16} />
3091
Trade
3192
</button>
@@ -35,6 +96,7 @@ export default function InvoiceDetailPage() {
3596

3697
<div className="px-4 lg:px-8 py-6">
3798
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
99+
{/* Left Column */}
38100
<div className="lg:col-span-2 space-y-6">
39101
{/* Invoice Overview */}
40102
<div className="bg-slate-800/50 rounded-xl border border-slate-700/50 p-6">
@@ -75,10 +137,8 @@ export default function InvoiceDetailPage() {
75137
</p>
76138
</div>
77139
<div>
78-
<p className="text-slate-400 text-sm mb-1">Issuer</p>
79-
<p className="font-mono text-sm truncate text-slate-300">
80-
{invoice?.issuer ?? "—"}
81-
</p>
140+
<p className="text-slate-400 text-sm mb-1">Maturity Date</p>
141+
<p className="font-medium">Dec 15, 2026</p>
82142
</div>
83143
<div>
84144
<p className="text-slate-400 text-sm mb-1">Status</p>
@@ -112,7 +172,10 @@ export default function InvoiceDetailPage() {
112172
{ date: "2024-01-01", type: "Payment", amount: "$2,125", status: "Completed" },
113173
{ date: "2023-12-15", type: "Initial", amount: "$50,000", status: "Completed" },
114174
].map((tx, index) => (
115-
<div key={index} className="flex items-center justify-between p-3 bg-slate-700/30 rounded-lg">
175+
<div
176+
key={index}
177+
className="flex items-center justify-between p-3 bg-slate-700/30 rounded-lg"
178+
>
116179
<div>
117180
<p className="font-medium">{tx.type}</p>
118181
<p className="text-slate-400 text-sm">{tx.date}</p>
@@ -135,19 +198,31 @@ export default function InvoiceDetailPage() {
135198
<h2 className="text-xl font-semibold">Risk Assessment</h2>
136199
</div>
137200
<div className="space-y-4">
138-
{[
139-
{ label: "Credit Score", value: "A+", width: "85%", color: "bg-green-500" },
140-
{ label: "Collateral Ratio", value: "150%", width: "75%", color: "bg-blue-500" },
141-
{ label: "Liquidity Score", value: "High", width: "90%", color: "bg-purple-500" },
142-
].map((item) => (
143-
<div key={item.label}>
144-
<div className="flex justify-between mb-1">
145-
<span className="text-sm">{item.label}</span>
146-
<span className="text-sm font-medium">{item.value}</span>
147-
</div>
148-
<div className="w-full bg-slate-700 h-2 rounded-full">
149-
<div className={`${item.color} h-2 rounded-full`} style={{ width: item.width }} />
150-
</div>
201+
<div>
202+
<div className="flex justify-between mb-1">
203+
<span className="text-sm">Credit Score</span>
204+
<span className="text-sm font-medium">A+</span>
205+
</div>
206+
<div className="w-full bg-slate-700 h-2 rounded-full">
207+
<div className="bg-green-500 h-2 rounded-full" style={{ width: "85%" }} />
208+
</div>
209+
</div>
210+
<div>
211+
<div className="flex justify-between mb-1">
212+
<span className="text-sm">Collateral Ratio</span>
213+
<span className="text-sm font-medium">150%</span>
214+
</div>
215+
<div className="w-full bg-slate-700 h-2 rounded-full">
216+
<div className="bg-blue-500 h-2 rounded-full" style={{ width: "75%" }} />
217+
</div>
218+
</div>
219+
<div>
220+
<div className="flex justify-between mb-1">
221+
<span className="text-sm">Liquidity Score</span>
222+
<span className="text-sm font-medium">High</span>
223+
</div>
224+
<div className="w-full bg-slate-700 h-2 rounded-full">
225+
<div className="bg-purple-500 h-2 rounded-full" style={{ width: "90%" }} />
151226
</div>
152227
))}
153228
</div>
@@ -156,8 +231,11 @@ export default function InvoiceDetailPage() {
156231
<div className="bg-slate-800/50 rounded-xl border border-slate-700/50 p-6">
157232
<h2 className="text-xl font-semibold mb-4">Quick Actions</h2>
158233
<div className="space-y-3">
159-
<button className="w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors">
160-
Buy More Tokens
234+
<button
235+
onClick={() => setShowBuyModal(true)}
236+
className="w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors"
237+
>
238+
Buy Fraction
161239
</button>
162240
<button className="w-full px-4 py-2 bg-slate-700 hover:bg-slate-600 rounded-lg transition-colors">
163241
View Documents
@@ -170,6 +248,16 @@ export default function InvoiceDetailPage() {
170248
</div>
171249
</div>
172250
</div>
251+
252+
{/* Fractional Purchase Modal */}
253+
{showBuyModal && (
254+
<FractionalPurchaseModal
255+
invoice={INVOICE_DATA}
256+
walletBalance={usdcBalance}
257+
onClose={() => setShowBuyModal(false)}
258+
onBuyFraction={handleBuyFraction}
259+
/>
260+
)}
173261
</div>
174262
);
175263
}

0 commit comments

Comments
 (0)