@microcosmmoney/portal-react 2.3.4 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/auction/auction-page.js +118 -25
- package/dist/components/dashboard/assets-summary.d.ts +2 -1
- package/dist/components/dashboard/assets-summary.js +11 -6
- package/dist/components/dashboard/dashboard-overview.d.ts +2 -1
- package/dist/components/dashboard/dashboard-overview.js +11 -2
- package/dist/components/dashboard/ecosystem-stats.d.ts +4 -1
- package/dist/components/dashboard/ecosystem-stats.js +12 -9
- package/dist/components/dashboard/lock-periods.d.ts +4 -1
- package/dist/components/dashboard/lock-periods.js +2 -2
- package/dist/components/dashboard/market-overview-bar.d.ts +4 -1
- package/dist/components/dashboard/market-overview-bar.js +9 -6
- package/dist/components/dashboard/mcc-token-stats.d.ts +4 -1
- package/dist/components/dashboard/mcc-token-stats.js +12 -9
- package/dist/components/dashboard/mcd-stats.d.ts +4 -1
- package/dist/components/dashboard/mcd-stats.js +14 -11
- package/dist/components/dashboard/mining-weight.d.ts +4 -1
- package/dist/components/dashboard/mining-weight.js +10 -7
- package/dist/components/dashboard/minting-stats.d.ts +4 -1
- package/dist/components/dashboard/minting-stats.js +6 -3
- package/dist/components/dashboard/my-mining.d.ts +2 -1
- package/dist/components/dashboard/my-mining.js +4 -2
- package/dist/components/dashboard/price-chart.d.ts +4 -1
- package/dist/components/dashboard/price-chart.js +6 -4
- package/dist/components/dashboard/quick-actions.d.ts +2 -1
- package/dist/components/dashboard/quick-actions.js +52 -37
- package/dist/components/fragment/fragment-page.js +53 -9
- package/dist/components/lending/lending-page.js +124 -22
- package/dist/components/mcd/mcd-page.js +139 -18
- package/dist/components/mining/mining-page.js +46 -15
- package/dist/components/organization/organization-page.js +90 -22
- package/dist/components/territory/territory-page.js +126 -29
- package/dist/components/voting/voting-page.js +10 -10
- package/dist/components/wallet/wallet-page.js +186 -23
- package/dist/index.d.ts +8 -0
- package/package.json +4 -4
|
@@ -6,16 +6,40 @@ exports.MicrocosmFragmentPage = MicrocosmFragmentPage;
|
|
|
6
6
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
7
|
const react_1 = require("react");
|
|
8
8
|
const auth_react_1 = require("@microcosmmoney/auth-react");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const formatNumber = (num, decimals = 2) => num.toLocaleString('en-US', { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
|
|
10
|
+
const formatMCC = (lamports) => formatNumber(lamports / 1000000000, 2);
|
|
11
|
+
/* ── Inline SVG Icons (lucide style) ── */
|
|
12
|
+
function IconRefreshCw({ className }) {
|
|
13
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("path", { d: "M21 2v6h-6" }), (0, jsx_runtime_1.jsx)("path", { d: "M3 12a9 9 0 0115.15-6.64L21 8" }), (0, jsx_runtime_1.jsx)("path", { d: "M3 22v-6h6" }), (0, jsx_runtime_1.jsx)("path", { d: "M21 12a9 9 0 01-15.15 6.64L3 16" })] }));
|
|
13
14
|
}
|
|
15
|
+
function IconWallet({ className }) {
|
|
16
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("path", { d: "M19 7V4a1 1 0 00-1-1H5a2 2 0 000 4h15a1 1 0 011 1v4h-3a2 2 0 000 4h3a1 1 0 001-1v-2a1 1 0 00-1-1" }), (0, jsx_runtime_1.jsx)("path", { d: "M3 5v14a2 2 0 002 2h15a1 1 0 001-1v-4" })] }));
|
|
17
|
+
}
|
|
18
|
+
function IconPuzzle({ className }) {
|
|
19
|
+
return ((0, jsx_runtime_1.jsx)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { d: "M11 4a2 2 0 114 0v1a1 1 0 001 1h3a1 1 0 011 1v3a1 1 0 01-1 1h-1a2 2 0 100 4h1a1 1 0 011 1v3a1 1 0 01-1 1h-3a1 1 0 01-1-1v-1a2 2 0 10-4 0v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-3a1 1 0 00-1-1H4a2 2 0 110-4h1a1 1 0 001-1V7a1 1 0 011-1h3a1 1 0 001-1V4z" }) }));
|
|
20
|
+
}
|
|
21
|
+
function IconImage({ className }) {
|
|
22
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }), (0, jsx_runtime_1.jsx)("circle", { cx: "8.5", cy: "8.5", r: "1.5" }), (0, jsx_runtime_1.jsx)("path", { d: "M21 15l-5-5L5 21" })] }));
|
|
23
|
+
}
|
|
24
|
+
function IconShoppingCart({ className }) {
|
|
25
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "9", cy: "21", r: "1" }), (0, jsx_runtime_1.jsx)("circle", { cx: "20", cy: "21", r: "1" }), (0, jsx_runtime_1.jsx)("path", { d: "M1 1h4l2.68 13.39a2 2 0 002 1.61h9.72a2 2 0 002-1.61L23 6H6" })] }));
|
|
26
|
+
}
|
|
27
|
+
function IconInfo({ className }) {
|
|
28
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "10" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 16v-4" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 8h.01" })] }));
|
|
29
|
+
}
|
|
30
|
+
function IconAlertTriangle({ className }) {
|
|
31
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("path", { d: "M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 9v4" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 17h.01" })] }));
|
|
32
|
+
}
|
|
33
|
+
function IconLoader({ className }) {
|
|
34
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("path", { d: "M12 2v4" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 18v4" }), (0, jsx_runtime_1.jsx)("path", { d: "M4.93 4.93l2.83 2.83" }), (0, jsx_runtime_1.jsx)("path", { d: "M16.24 16.24l2.83 2.83" }), (0, jsx_runtime_1.jsx)("path", { d: "M2 12h4" }), (0, jsx_runtime_1.jsx)("path", { d: "M18 12h4" }), (0, jsx_runtime_1.jsx)("path", { d: "M4.93 19.07l2.83-2.83" }), (0, jsx_runtime_1.jsx)("path", { d: "M16.24 7.76l2.83-2.83" })] }));
|
|
35
|
+
}
|
|
36
|
+
/* ── Modal ── */
|
|
14
37
|
function Modal({ open, onClose, children }) {
|
|
15
38
|
if (!open)
|
|
16
39
|
return null;
|
|
17
40
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center", onClick: onClose, children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 bg-black/60" }), (0, jsx_runtime_1.jsx)("div", { className: "relative bg-neutral-900 border border-neutral-700 rounded-lg p-6 max-w-md w-full mx-4 font-mono", onClick: e => e.stopPropagation(), children: children })] }));
|
|
18
41
|
}
|
|
42
|
+
/* ── Main Component ── */
|
|
19
43
|
function MicrocosmFragmentPage({ onNavigate }) {
|
|
20
44
|
const { data: wallets } = (0, auth_react_1.useWallets)();
|
|
21
45
|
const primaryWallet = wallets?.[0]?.wallet_address;
|
|
@@ -28,15 +52,23 @@ function MicrocosmFragmentPage({ onNavigate }) {
|
|
|
28
52
|
const [refreshing, setRefreshing] = (0, react_1.useState)(false);
|
|
29
53
|
const [actionError, setActionError] = (0, react_1.useState)(null);
|
|
30
54
|
const [actionSuccess, setActionSuccess] = (0, react_1.useState)(null);
|
|
55
|
+
const [initialLoaded, setInitialLoaded] = (0, react_1.useState)(false);
|
|
31
56
|
const loading = vLoading || hLoading;
|
|
57
|
+
(0, react_1.useEffect)(() => {
|
|
58
|
+
if (!loading && !initialLoaded) {
|
|
59
|
+
setInitialLoaded(true);
|
|
60
|
+
}
|
|
61
|
+
}, [loading, initialLoaded]);
|
|
32
62
|
const handleRefresh = (0, react_1.useCallback)(async () => {
|
|
33
63
|
setRefreshing(true);
|
|
34
64
|
await Promise.all([refreshVaults(), refreshHoldings()]);
|
|
35
65
|
setRefreshing(false);
|
|
36
66
|
}, [refreshVaults, refreshHoldings]);
|
|
37
67
|
const handleBuy = async () => {
|
|
38
|
-
if (!selectedVault)
|
|
68
|
+
if (!selectedVault) {
|
|
69
|
+
setActionError('Please select a vault');
|
|
39
70
|
return;
|
|
71
|
+
}
|
|
40
72
|
const amount = parseInt(buyAmount);
|
|
41
73
|
if (isNaN(amount) || amount <= 0) {
|
|
42
74
|
setActionError('Please enter a valid amount');
|
|
@@ -55,8 +87,20 @@ function MicrocosmFragmentPage({ onNavigate }) {
|
|
|
55
87
|
setActionError(err instanceof Error ? err.message : 'Purchase failed');
|
|
56
88
|
}
|
|
57
89
|
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
90
|
+
/* ── Loading state (matches portal full-page spinner) ── */
|
|
91
|
+
if (!initialLoaded && loading) {
|
|
92
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto font-mono space-y-6", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold text-white", children: "Fragment" }), (0, jsx_runtime_1.jsx)("p", { className: "text-neutral-400 text-sm mt-1", children: "NFT fractionalization protocol" })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center py-8", children: (0, jsx_runtime_1.jsx)(IconLoader, { className: "w-5 h-5 animate-spin text-cyan-400" }) })] }));
|
|
93
|
+
}
|
|
94
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto font-mono space-y-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold text-white", children: "Fragment" }), (0, jsx_runtime_1.jsx)("p", { className: "text-neutral-400 text-sm mt-1", children: "NFT fractionalization protocol" })] }), (0, jsx_runtime_1.jsxs)("button", { onClick: handleRefresh, disabled: refreshing, className: "flex items-center gap-1 px-3 py-1.5 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 disabled:opacity-50 bg-transparent", children: [(0, jsx_runtime_1.jsx)(IconRefreshCw, { className: `w-4 h-4 ${refreshing ? 'animate-spin' : ''}` }), "Refresh"] })] }), actionError && ((0, jsx_runtime_1.jsxs)("div", { className: "bg-red-500/10 border border-red-500/20 rounded-lg p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-red-500 mb-2", children: [(0, jsx_runtime_1.jsx)(IconAlertTriangle, { className: "w-5 h-5" }), (0, jsx_runtime_1.jsx)("span", { className: "font-medium", children: actionError })] }), (0, jsx_runtime_1.jsx)("p", { className: "text-neutral-400 text-sm", children: "Please check the details and try again." })] })), actionSuccess && ((0, jsx_runtime_1.jsx)("div", { className: "p-3 bg-cyan-400/10 border border-cyan-400/30 rounded text-cyan-400 text-sm", children: actionSuccess })), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-neutral-400 text-sm mb-4", children: [(0, jsx_runtime_1.jsx)(IconWallet, { className: "w-4 h-4" }), (0, jsx_runtime_1.jsx)("span", { children: "MY_HOLDINGS" })] }), holdings && holdings.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: holdings.map((h, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between p-4 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "p-2 bg-cyan-400/20 rounded border border-cyan-400/30", children: (0, jsx_runtime_1.jsx)(IconPuzzle, { className: "h-5 w-5 text-cyan-400" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "font-bold text-white font-mono", children: ["Vault #", h.vault_id] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-neutral-400", children: [(h.percentage ?? 0).toFixed(2), "% ownership"] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-right", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-white font-mono", children: formatNumber(h.fragment_amount ?? 0, 0) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400", children: "fragments" })] })] }, `${h.vault_id}-${h.wallet ?? i}`))) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "text-center py-8 text-neutral-400", children: [(0, jsx_runtime_1.jsx)(IconPuzzle, { className: "w-12 h-12 mx-auto mb-2 opacity-50" }), (0, jsx_runtime_1.jsx)("p", { children: "No fragment holdings" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm mt-1", children: "Purchase fragments from available vaults below" })] }))] }) }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-between mb-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-neutral-400 text-sm", children: [(0, jsx_runtime_1.jsx)(IconImage, { className: "w-4 h-4" }), (0, jsx_runtime_1.jsx)("span", { children: "FRAGMENT_VAULTS" })] }) }), vaults && vaults.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-4", children: vaults.map((v) => {
|
|
95
|
+
const soldPercentage = v.total_fragments > 0
|
|
96
|
+
? (v.fragments_sold / v.total_fragments) * 100
|
|
97
|
+
: 0;
|
|
98
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-start mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "p-2 bg-cyan-400/20 rounded border border-cyan-400/30", children: (0, jsx_runtime_1.jsx)(IconImage, { className: "h-6 w-6 text-cyan-400" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "font-bold text-white", children: [v.nft_type ?? 'Territory', " NFT"] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-neutral-400", children: [(v.nft_mint ?? '').slice(0, 8), "...", (v.nft_mint ?? '').slice(-4)] })] })] }), (0, jsx_runtime_1.jsx)("span", { className: `px-2 py-0.5 rounded text-xs ${v.is_active ? 'bg-white/20 text-white' : 'bg-neutral-700/50 text-neutral-400'}`, children: v.is_active ? 'OPEN' : 'CLOSED' })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider", children: "total_fragments" }), (0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-white font-mono", children: formatNumber(v.total_fragments ?? 0, 0) })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider", children: "price_per_fragment" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-lg font-bold text-cyan-400 font-mono", children: [formatMCC(v.price_per_fragment ?? 0), " MCC"] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider", children: "sold" }), (0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-cyan-300 font-mono", children: formatNumber(v.fragments_sold ?? 0, 0) })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider", children: "available" }), (0, jsx_runtime_1.jsx)("div", { className: "text-lg font-bold text-white font-mono", children: formatNumber((v.total_fragments ?? 0) - (v.fragments_sold ?? 0), 0) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "sale_progress" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-sm font-mono text-white", children: [soldPercentage.toFixed(1), "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "w-full bg-neutral-800 rounded-full h-2", children: (0, jsx_runtime_1.jsx)("div", { className: "bg-cyan-400 h-2 rounded-full transition-all", style: { width: `${Math.min(soldPercentage, 100)}%` } }) })] }), v.is_active && (v.fragments_sold ?? 0) < (v.total_fragments ?? 0) && ((0, jsx_runtime_1.jsx)("div", { className: "flex justify-end", children: (0, jsx_runtime_1.jsxs)("button", { onClick: () => {
|
|
99
|
+
setSelectedVault(v);
|
|
100
|
+
setBuyDialogOpen(true);
|
|
101
|
+
setActionError(null);
|
|
102
|
+
}, className: "flex items-center gap-1 px-4 py-2 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded font-mono", children: [(0, jsx_runtime_1.jsx)(IconShoppingCart, { className: "w-4 h-4" }), "Buy Fragments"] }) }))] }, v.vault_id));
|
|
103
|
+
}) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "text-center py-8 text-neutral-400", children: [(0, jsx_runtime_1.jsx)(IconImage, { className: "w-12 h-12 mx-auto mb-2 opacity-50" }), (0, jsx_runtime_1.jsx)("p", { children: "No fragment vaults available" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm mt-1", children: "Check back later for new NFT fragmentation opportunities" })] }))] }) }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-neutral-400 text-sm mb-4", children: [(0, jsx_runtime_1.jsx)(IconInfo, { className: "w-4 h-4" }), (0, jsx_runtime_1.jsx)("span", { children: "PROTOCOL_INFO" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-white mb-2", children: "What is Fragmentation?" }), (0, jsx_runtime_1.jsx)("p", { className: "text-neutral-400", children: "Fragment allows NFT owners to split Territory NFTs into tradeable fragments, enabling shared ownership and community buyout mechanics." })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-white mb-2", children: "Fragment Holder Rights" }), (0, jsx_runtime_1.jsxs)("ul", { className: "list-disc list-inside space-y-1 text-neutral-400", children: [(0, jsx_runtime_1.jsx)("li", { children: "Proportional ownership of the underlying NFT" }), (0, jsx_runtime_1.jsx)("li", { children: "Participate in buyout proposals" }), (0, jsx_runtime_1.jsx)("li", { children: "Trade fragments freely on the market" })] })] })] })] }) }), (0, jsx_runtime_1.jsx)(Modal, { open: buyDialogOpen, onClose: () => setBuyDialogOpen(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-bold text-white", children: "Buy Fragments" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400 mt-1", children: selectedVault
|
|
104
|
+
? `Purchase fragments from ${selectedVault.nft_type ?? 'Territory'} NFT vault`
|
|
105
|
+
: 'Select a vault' })] }), selectedVault && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Unit Price" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-cyan-400 font-bold font-mono", children: [formatMCC(selectedVault.price_per_fragment ?? 0), " MCC"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Available" }), (0, jsx_runtime_1.jsx)("span", { className: "text-white font-bold font-mono", children: formatNumber((selectedVault.total_fragments ?? 0) - (selectedVault.fragments_sold ?? 0), 0) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-xs text-neutral-400 tracking-wider", children: "purchase_amount" }), (0, jsx_runtime_1.jsx)("input", { type: "number", step: "1", min: "1", value: buyAmount, onChange: e => setBuyAmount(e.target.value), placeholder: "Enter amount", className: "w-full px-3 py-2 bg-neutral-800 border border-neutral-600 rounded text-white placeholder-neutral-400 outline-none focus:border-cyan-400" })] }), buyAmount && parseInt(buyAmount) > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Total Price" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white font-bold font-mono", children: [formatMCC((selectedVault.price_per_fragment ?? 0) * parseInt(buyAmount)), " MCC"] })] }) }))] })), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-3 pt-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => { setBuyDialogOpen(false); setBuyAmount(''); }, className: "px-4 py-2 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 bg-transparent", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleBuy, disabled: actionLoading || !buyAmount, className: "flex items-center gap-2 px-4 py-2 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded disabled:opacity-50", children: actionLoading ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(IconLoader, { className: "h-4 w-4 animate-spin" }), "Buying..."] })) : ('Confirm Purchase') })] })] }) })] }));
|
|
62
106
|
}
|
|
@@ -6,17 +6,69 @@ exports.MicrocosmLendingPage = MicrocosmLendingPage;
|
|
|
6
6
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
7
|
const react_1 = require("react");
|
|
8
8
|
const auth_react_1 = require("@microcosmmoney/auth-react");
|
|
9
|
+
/* ── helpers ─────────────────────────────────────────────────── */
|
|
9
10
|
const fmt = (num, decimals = 2) => num.toLocaleString('en-US', { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
|
|
10
11
|
const fmtMCC = (lamports) => fmt(lamports / 1000000000, 2);
|
|
12
|
+
const NFT_TYPE_MAP = {
|
|
13
|
+
Station: 'Station',
|
|
14
|
+
Matrix: 'Matrix',
|
|
15
|
+
Sector: 'Sector',
|
|
16
|
+
System: 'System',
|
|
17
|
+
};
|
|
11
18
|
const COLLATERAL_TYPES = {
|
|
12
|
-
Station: { value: 1000, label: 'Station' },
|
|
13
|
-
Matrix: { value: 15000, label: 'Matrix' },
|
|
14
|
-
Sector: { value: 200000, label: 'Sector' },
|
|
15
|
-
System: { value: 2500000, label: 'System' },
|
|
19
|
+
Station: { value: 1000, label: 'Station', description: 'Space Station (1,000 users)' },
|
|
20
|
+
Matrix: { value: 15000, label: 'Matrix', description: 'Matrix (10 Stations)' },
|
|
21
|
+
Sector: { value: 200000, label: 'Sector', description: 'Sector (10 Matrices)' },
|
|
22
|
+
System: { value: 2500000, label: 'System', description: 'System (10 Sectors)' },
|
|
16
23
|
};
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
/* ── inline SVG icons (lucide-style) ─────────────────────────── */
|
|
25
|
+
function IconLandmark({ className }) {
|
|
26
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("line", { x1: "3", y1: "22", x2: "21", y2: "22" }), (0, jsx_runtime_1.jsx)("line", { x1: "6", y1: "18", x2: "6", y2: "11" }), (0, jsx_runtime_1.jsx)("line", { x1: "10", y1: "18", x2: "10", y2: "11" }), (0, jsx_runtime_1.jsx)("line", { x1: "14", y1: "18", x2: "14", y2: "11" }), (0, jsx_runtime_1.jsx)("line", { x1: "18", y1: "18", x2: "18", y2: "11" }), (0, jsx_runtime_1.jsx)("polygon", { points: "12 2 20 7 4 7" })] }));
|
|
27
|
+
}
|
|
28
|
+
function IconWallet({ className }) {
|
|
29
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("path", { d: "M21 12V7H5a2 2 0 010-4h14v4" }), (0, jsx_runtime_1.jsx)("path", { d: "M3 5v14a2 2 0 002 2h16v-5" }), (0, jsx_runtime_1.jsx)("path", { d: "M18 12a1 1 0 100 2 1 1 0 000-2z" })] }));
|
|
30
|
+
}
|
|
31
|
+
function IconTrendingUp({ className }) {
|
|
32
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("polyline", { points: "23 6 13.5 15.5 8.5 10.5 1 18" }), (0, jsx_runtime_1.jsx)("polyline", { points: "17 6 23 6 23 12" })] }));
|
|
33
|
+
}
|
|
34
|
+
function IconPiggyBank({ className }) {
|
|
35
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("path", { d: "M19 5c-1.5 0-2.8 1.4-3 2-3.5-1.5-11-.3-11 5 0 1.8 0 3 2 4.5V20h4v-2h3v2h4v-4c1-.5 1.7-1 2-2h2v-4h-2c0-1-.5-1.5-1-2" }), (0, jsx_runtime_1.jsx)("path", { d: "M2 9.5a.5.5 0 111 0 .5.5 0 01-1 0" }), (0, jsx_runtime_1.jsx)("path", { d: "M14 4a2 2 0 00-2 2" })] }));
|
|
36
|
+
}
|
|
37
|
+
function IconCreditCard({ className }) {
|
|
38
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("rect", { x: "1", y: "4", width: "22", height: "16", rx: "2", ry: "2" }), (0, jsx_runtime_1.jsx)("line", { x1: "1", y1: "10", x2: "23", y2: "10" })] }));
|
|
39
|
+
}
|
|
40
|
+
function IconAlertTriangle({ className }) {
|
|
41
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("path", { d: "M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" }), (0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "9", x2: "12", y2: "13" }), (0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })] }));
|
|
42
|
+
}
|
|
43
|
+
function IconClock({ className }) {
|
|
44
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "10" }), (0, jsx_runtime_1.jsx)("polyline", { points: "12 6 12 12 16 14" })] }));
|
|
45
|
+
}
|
|
46
|
+
function IconRefreshCw({ className }) {
|
|
47
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("polyline", { points: "23 4 23 10 17 10" }), (0, jsx_runtime_1.jsx)("polyline", { points: "1 20 1 14 7 14" }), (0, jsx_runtime_1.jsx)("path", { d: "M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15" })] }));
|
|
48
|
+
}
|
|
49
|
+
function IconInfo({ className }) {
|
|
50
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "10" }), (0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "16", x2: "12", y2: "12" }), (0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })] }));
|
|
51
|
+
}
|
|
52
|
+
function IconPlus({ className }) {
|
|
53
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), (0, jsx_runtime_1.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }));
|
|
19
54
|
}
|
|
55
|
+
function IconMinus({ className }) {
|
|
56
|
+
return ((0, jsx_runtime_1.jsx)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" }) }));
|
|
57
|
+
}
|
|
58
|
+
function IconImage({ className }) {
|
|
59
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }), (0, jsx_runtime_1.jsx)("circle", { cx: "8.5", cy: "8.5", r: "1.5" }), (0, jsx_runtime_1.jsx)("polyline", { points: "21 15 16 10 5 21" })] }));
|
|
60
|
+
}
|
|
61
|
+
function IconCheckCircle({ className }) {
|
|
62
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("path", { d: "M22 11.08V12a10 10 0 11-5.93-9.14" }), (0, jsx_runtime_1.jsx)("polyline", { points: "22 4 12 14.01 9 11.01" })] }));
|
|
63
|
+
}
|
|
64
|
+
function IconLoader({ className }) {
|
|
65
|
+
return ((0, jsx_runtime_1.jsxs)("svg", { className: className, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "2", x2: "12", y2: "6" }), (0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), (0, jsx_runtime_1.jsx)("line", { x1: "4.93", y1: "4.93", x2: "7.76", y2: "7.76" }), (0, jsx_runtime_1.jsx)("line", { x1: "16.24", y1: "16.24", x2: "19.07", y2: "19.07" }), (0, jsx_runtime_1.jsx)("line", { x1: "2", y1: "12", x2: "6", y2: "12" }), (0, jsx_runtime_1.jsx)("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), (0, jsx_runtime_1.jsx)("line", { x1: "4.93", y1: "19.07", x2: "7.76", y2: "16.24" }), (0, jsx_runtime_1.jsx)("line", { x1: "16.24", y1: "7.76", x2: "19.07", y2: "4.93" })] }));
|
|
66
|
+
}
|
|
67
|
+
/* ── cn helper ───────────────────────────────────────────────── */
|
|
68
|
+
function cn(...classes) {
|
|
69
|
+
return classes.filter(Boolean).join(' ');
|
|
70
|
+
}
|
|
71
|
+
/* ── Modal (inline, no shadcn) ───────────────────────────────── */
|
|
20
72
|
function Modal({ open, onClose, children }) {
|
|
21
73
|
if (!open)
|
|
22
74
|
return null;
|
|
@@ -42,18 +94,39 @@ function MicrocosmLendingPage({ onNavigate }) {
|
|
|
42
94
|
const [withdrawAmount, setWithdrawAmount] = (0, react_1.useState)('');
|
|
43
95
|
const [selectedNFT, setSelectedNFT] = (0, react_1.useState)('');
|
|
44
96
|
const [borrowAmount, setBorrowAmount] = (0, react_1.useState)('');
|
|
97
|
+
const [selectedLoan, setSelectedLoan] = (0, react_1.useState)(null);
|
|
45
98
|
const [repayAmount, setRepayAmount] = (0, react_1.useState)('');
|
|
99
|
+
// Loans loaded from pool/stats data or nft-based lookup
|
|
100
|
+
const [userLoans, setUserLoans] = (0, react_1.useState)([]);
|
|
46
101
|
const loading = poolLoading || statsLoading || lpLoading;
|
|
47
102
|
const poolData = pool ?? stats;
|
|
103
|
+
const isSubmitting = actionLoading;
|
|
104
|
+
// Clear status messages after 5s
|
|
105
|
+
(0, react_1.useEffect)(() => {
|
|
106
|
+
if (actionSuccess) {
|
|
107
|
+
const t = setTimeout(() => setActionSuccess(null), 5000);
|
|
108
|
+
return () => clearTimeout(t);
|
|
109
|
+
}
|
|
110
|
+
}, [actionSuccess]);
|
|
111
|
+
(0, react_1.useEffect)(() => {
|
|
112
|
+
if (actionError) {
|
|
113
|
+
const t = setTimeout(() => setActionError(null), 8000);
|
|
114
|
+
return () => clearTimeout(t);
|
|
115
|
+
}
|
|
116
|
+
}, [actionError]);
|
|
117
|
+
// Derive available NFTs (exclude collateralized ones)
|
|
118
|
+
const collateralizedMints = new Set(userLoans.filter(l => l.status === 'Active').map(l => l.collateral_nft_mint));
|
|
119
|
+
const availableNFTs = (nfts ?? []).filter((nft) => !collateralizedMints.has(nft.mint));
|
|
48
120
|
const handleRefresh = (0, react_1.useCallback)(async () => {
|
|
49
121
|
setRefreshing(true);
|
|
50
122
|
await Promise.all([refreshPool(), refreshStats(), refreshLP(), refreshOracle(), refreshNFTs()]);
|
|
51
123
|
setRefreshing(false);
|
|
52
124
|
}, [refreshPool, refreshStats, refreshLP, refreshOracle, refreshNFTs]);
|
|
125
|
+
/* ── Action handlers ─────────────────────────────────────── */
|
|
53
126
|
const handleDeposit = async () => {
|
|
54
127
|
const amount = parseFloat(depositAmount);
|
|
55
128
|
if (isNaN(amount) || amount <= 0) {
|
|
56
|
-
setActionError('Enter a valid amount');
|
|
129
|
+
setActionError('Enter a valid deposit amount');
|
|
57
130
|
return;
|
|
58
131
|
}
|
|
59
132
|
try {
|
|
@@ -71,7 +144,7 @@ function MicrocosmLendingPage({ onNavigate }) {
|
|
|
71
144
|
const handleWithdraw = async () => {
|
|
72
145
|
const amount = parseFloat(withdrawAmount);
|
|
73
146
|
if (isNaN(amount) || amount <= 0) {
|
|
74
|
-
setActionError('Enter a valid amount');
|
|
147
|
+
setActionError('Enter a valid withdraw amount');
|
|
75
148
|
return;
|
|
76
149
|
}
|
|
77
150
|
try {
|
|
@@ -88,20 +161,20 @@ function MicrocosmLendingPage({ onNavigate }) {
|
|
|
88
161
|
};
|
|
89
162
|
const handleBorrow = async () => {
|
|
90
163
|
if (!selectedNFT) {
|
|
91
|
-
setActionError('Select an NFT');
|
|
164
|
+
setActionError('Select an NFT as collateral');
|
|
92
165
|
return;
|
|
93
166
|
}
|
|
94
167
|
const amount = parseFloat(borrowAmount);
|
|
95
168
|
if (isNaN(amount) || amount <= 0) {
|
|
96
|
-
setActionError('Enter a valid amount');
|
|
169
|
+
setActionError('Enter a valid borrow amount');
|
|
97
170
|
return;
|
|
98
171
|
}
|
|
99
|
-
const nft =
|
|
172
|
+
const nft = availableNFTs.find((n) => n.mint === selectedNFT);
|
|
100
173
|
if (!nft) {
|
|
101
174
|
setActionError('NFT not found');
|
|
102
175
|
return;
|
|
103
176
|
}
|
|
104
|
-
const nftType = nft.nft_type || 'Station';
|
|
177
|
+
const nftType = NFT_TYPE_MAP[nft.nft_type] || 'Station';
|
|
105
178
|
const maxBorrow = COLLATERAL_TYPES[nftType]?.value ?? 1000;
|
|
106
179
|
if (amount > maxBorrow) {
|
|
107
180
|
setActionError(`Max borrow: ${fmt(maxBorrow)} MCC`);
|
|
@@ -110,7 +183,7 @@ function MicrocosmLendingPage({ onNavigate }) {
|
|
|
110
183
|
try {
|
|
111
184
|
setActionError(null);
|
|
112
185
|
await borrow({ wallet: primaryWallet ?? '', amount: amount * 1000000000, nft_mint: nft.mint, duration_type: 1 });
|
|
113
|
-
setActionSuccess(`Borrowed ${fmt(amount)} MCC`);
|
|
186
|
+
setActionSuccess(`Borrowed ${fmt(amount)} MCC against ${nft.name}`);
|
|
114
187
|
setBorrowOpen(false);
|
|
115
188
|
setSelectedNFT('');
|
|
116
189
|
setBorrowAmount('');
|
|
@@ -121,16 +194,23 @@ function MicrocosmLendingPage({ onNavigate }) {
|
|
|
121
194
|
}
|
|
122
195
|
};
|
|
123
196
|
const handleRepay = async () => {
|
|
197
|
+
if (!selectedLoan) {
|
|
198
|
+
setActionError('Select a loan to repay');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
124
201
|
const amount = parseFloat(repayAmount);
|
|
125
202
|
if (isNaN(amount) || amount <= 0) {
|
|
126
|
-
setActionError('Enter a valid amount');
|
|
203
|
+
setActionError('Enter a valid repay amount');
|
|
127
204
|
return;
|
|
128
205
|
}
|
|
129
206
|
try {
|
|
130
207
|
setActionError(null);
|
|
131
|
-
await repay({ wallet: primaryWallet ?? '', nft_mint:
|
|
132
|
-
|
|
208
|
+
await repay({ wallet: primaryWallet ?? '', nft_mint: selectedLoan.collateral_nft_mint, amount: amount * 1000000000 });
|
|
209
|
+
const totalDebt = selectedLoan.total_owed / 1000000000;
|
|
210
|
+
const isFullRepay = amount >= totalDebt;
|
|
211
|
+
setActionSuccess(isFullRepay ? 'Loan fully repaid! NFT returned to wallet.' : `Repaid ${fmt(amount)} MCC`);
|
|
133
212
|
setRepayOpen(false);
|
|
213
|
+
setSelectedLoan(null);
|
|
134
214
|
setRepayAmount('');
|
|
135
215
|
handleRefresh();
|
|
136
216
|
}
|
|
@@ -138,10 +218,32 @@ function MicrocosmLendingPage({ onNavigate }) {
|
|
|
138
218
|
setActionError(err instanceof Error ? err.message : 'Repay failed');
|
|
139
219
|
}
|
|
140
220
|
};
|
|
141
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto font-mono space-y-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold text-white tracking-wider", children: "Lending" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400", children: "MCC collateralized lending protocol" })] }), (0, jsx_runtime_1.jsxs)("button", { onClick: handleRefresh, disabled: refreshing, className: "flex items-center gap-2 px-3 py-1.5 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 disabled:opacity-50 bg-transparent", children: [(0, jsx_runtime_1.jsx)("svg", { className: `w-4 h-4 ${refreshing ? 'animate-spin' : ''}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) }), "Refresh"] })] }), actionError && ((0, jsx_runtime_1.jsx)("div", { className: "p-3 bg-red-500/10 border border-red-500/30 rounded text-red-400 text-sm", children: actionError })), actionSuccess && ((0, jsx_runtime_1.jsx)("div", { className: "p-3 bg-cyan-400/10 border border-cyan-400/30 rounded text-cyan-400 text-sm", children: actionSuccess })), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4 mb-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-cyan-400/20 rounded-xl border border-cyan-400/30", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-8 w-8 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 14v3m4-3v3m4-3v3M3 21h18M3 10h18M3 7l9-4 9 4M4 10h16v11H4V10z" }) }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "pool_name" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white", children: "MCC Lending Pool" })] })] }), loading ? ((0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-center py-8", children: (0, jsx_runtime_1.jsx)(Spinner, {}) })) : poolData ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mb-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)("svg", { className: "h-4 w-4 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" }) }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "total_deposits" })] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xl font-bold text-white font-mono", children: fmtMCC(poolData.total_deposits ?? 0) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "MCC" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)("svg", { className: "h-4 w-4 text-cyan-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" }) }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "total_borrowed" })] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xl font-bold text-cyan-400 font-mono", children: fmtMCC(poolData.total_borrows ?? 0) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "MCC" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)("svg", { className: "h-4 w-4 text-white", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" }) }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "deposit_apr" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xl font-bold text-white font-mono", children: [(poolData.supply_apr_percent ?? 0).toFixed(2), "%"] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "annualized" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)("svg", { className: "h-4 w-4 text-cyan-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" }) }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "borrow_apr" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xl font-bold text-cyan-400 font-mono", children: [(poolData.borrow_apr_percent ?? 0).toFixed(2), "%"] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "annualized" })] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "utilization_rate" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-sm font-mono text-white", children: [(poolData.utilization_rate_percent ?? 0).toFixed(1), "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "w-full bg-neutral-700 rounded-full h-2", children: (0, jsx_runtime_1.jsx)("div", { className: "bg-cyan-400 h-2 rounded-full transition-all", style: { width: `${Math.min(poolData.utilization_rate_percent ?? 0, 100)}%` } }) })] })] })) : ((0, jsx_runtime_1.jsx)("div", { className: "text-center py-8 text-neutral-500 font-mono text-sm", children: "pool data unavailable" }))] }) }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400 text-sm tracking-wider", children: "MY_DEPOSITS" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => { setDepositOpen(true); setActionError(null); }, className: "px-3 py-1.5 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded", children: "+ Deposit" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => { setWithdrawOpen(true); setActionError(null); }, disabled: !lpData || (lpData.lp_balance ?? 0) <= 0, className: "px-3 py-1.5 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 disabled:opacity-50 bg-transparent", children: "- Withdraw" })] })] }), lpData && (lpData.lp_balance ?? 0) > 0 ? ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "lp_token_balance" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white font-mono", children: fmtMCC(lpData.lp_balance ?? 0) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "LP Token" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "deposit_value" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white font-mono", children: fmtMCC(lpData.lp_value_in_mcc ?? lpData.lp_balance ?? 0) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "MCC" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "earned_interest" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-2xl font-bold text-cyan-400 font-mono", children: ["+", fmtMCC(Math.max(0, (lpData.lp_value_in_mcc ?? lpData.lp_balance ?? 0) - (lpData.lp_balance ?? 0)))] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "MCC" })] })] })) : ((0, jsx_runtime_1.jsx)("div", { className: "text-center py-8 text-neutral-500 font-mono text-sm", children: "no deposits yet \u2014 deposit MCC to earn interest" }))] }) }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400 text-sm tracking-wider", children: "MY_LOANS" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => { setBorrowOpen(true); setActionError(null); }, disabled: !nfts || nfts.length === 0, className: "px-3 py-1.5 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded disabled:opacity-50", children: "+ New Loan" })] }), (0, jsx_runtime_1.jsx)("div", { className: "text-center py-8 text-neutral-500 font-mono text-sm", children: "no active loans \u2014 pledge Territory NFT to borrow MCC" })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-neutral-400 text-sm mb-4", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }), (0, jsx_runtime_1.jsx)("span", { children: "PROTOCOL_INFO" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-white mb-2", children: "Loan Rules" }), (0, jsx_runtime_1.jsxs)("ul", { className: "list-disc list-inside space-y-1 text-neutral-400", children: [(0, jsx_runtime_1.jsx)("li", { children: "Collateral: Territory NFT (Station/Matrix/Sector/System)" }), (0, jsx_runtime_1.jsx)("li", { children: "Max LTV: 100% of NFT valuation" }), (0, jsx_runtime_1.jsx)("li", { children: "Interest: variable rate based on pool utilization" }), (0, jsx_runtime_1.jsx)("li", { children: "Duration: 3 / 7 / 30 days with auto-renewal" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-white mb-2", children: "Liquidation Rules" }), (0, jsx_runtime_1.jsxs)("ul", { className: "list-disc list-inside space-y-1 text-neutral-400", children: [(0, jsx_runtime_1.jsx)("li", { children: "LTV exceeds 90%: liquidation warning" }), (0, jsx_runtime_1.jsx)("li", { children: "Loan overdue or 2+ missed payments: liquidation eligible" }), (0, jsx_runtime_1.jsx)("li", { children: "Collateral NFT seized upon liquidation" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-white mb-2", children: "NFT Valuation" }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mt-2", children: Object.entries(COLLATERAL_TYPES).map(([key, val]) => ((0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-neutral-400 text-xs", children: val.label }), (0, jsx_runtime_1.jsx)("div", { className: "text-white font-bold font-mono", children: fmt(val.value) }), (0, jsx_runtime_1.jsx)("div", { className: "text-neutral-500 text-xs", children: "MCC" })] }, key))) })] })] })] }) }), (0, jsx_runtime_1.jsx)(Modal, { open: depositOpen, onClose: () => setDepositOpen(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-bold text-white", children: "Deposit MCC" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400 mt-1", children: "Earn interest by providing liquidity to the lending pool" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-cyan-400/10 border border-cyan-400/30 rounded", children: (0, jsx_runtime_1.jsxs)("div", { className: "text-sm text-cyan-400", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium mb-1", children: "Earnings Info" }), (0, jsx_runtime_1.jsxs)("ul", { className: "list-disc list-inside space-y-1 text-xs text-cyan-400/70", children: [(0, jsx_runtime_1.jsxs)("li", { children: ["Current APR: ", (poolData?.supply_apr_percent ?? 0).toFixed(2), "%"] }), (0, jsx_runtime_1.jsx)("li", { children: "Interest accrues in real-time" }), (0, jsx_runtime_1.jsx)("li", { children: "Withdraw anytime" })] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-xs text-neutral-400 tracking-wider", children: "amount (MCC)" }), (0, jsx_runtime_1.jsx)("input", { type: "number", step: "0.01", value: depositAmount, onChange: e => setDepositAmount(e.target.value), placeholder: "Enter amount", className: "w-full px-3 py-2 bg-neutral-800 border border-neutral-600 rounded text-white placeholder-neutral-400 outline-none focus:border-cyan-400" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-3 pt-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setDepositOpen(false), className: "px-4 py-2 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 bg-transparent", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleDeposit, disabled: actionLoading || !depositAmount, className: "px-4 py-2 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded disabled:opacity-50", children: actionLoading ? 'Processing...' : 'Confirm Deposit' })] })] }) }), (0, jsx_runtime_1.jsx)(Modal, { open: withdrawOpen, onClose: () => setWithdrawOpen(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-bold text-white", children: "Withdraw MCC" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400 mt-1", children: "Withdraw deposited MCC from the lending pool" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Withdrawable" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white font-bold font-mono", children: [fmtMCC(lpData?.lp_value_in_mcc ?? lpData?.lp_balance ?? 0), " MCC"] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-xs text-neutral-400 tracking-wider", children: "amount (MCC)" }), (0, jsx_runtime_1.jsx)("input", { type: "number", step: "0.01", value: withdrawAmount, onChange: e => setWithdrawAmount(e.target.value), placeholder: "Enter amount", className: "w-full px-3 py-2 bg-neutral-800 border border-neutral-600 rounded text-white placeholder-neutral-400 outline-none focus:border-cyan-400" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-3 pt-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setWithdrawOpen(false), className: "px-4 py-2 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 bg-transparent", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleWithdraw, disabled: actionLoading || !withdrawAmount, className: "px-4 py-2 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded disabled:opacity-50", children: actionLoading ? 'Processing...' : 'Confirm Withdraw' })] })] }) }), (0, jsx_runtime_1.jsx)(Modal, { open: borrowOpen, onClose: () => setBorrowOpen(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-bold text-white", children: "New Loan" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400 mt-1", children: "Borrow MCC using Territory NFT as collateral" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-xs text-neutral-400 tracking-wider", children: "select_nft" }), (0, jsx_runtime_1.jsxs)("select", { value: selectedNFT, onChange: e => setSelectedNFT(e.target.value), className: "w-full px-3 py-2 bg-neutral-800 border border-neutral-600 rounded text-white outline-none focus:border-cyan-400", children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Select Territory NFT..." }), (nfts ?? []).map((nft) => ((0, jsx_runtime_1.jsxs)("option", { value: nft.mint, children: [nft.name, " (", COLLATERAL_TYPES[nft.nft_type]?.label ?? nft.nft_type, ")"] }, nft.mint)))] })] }), selectedNFT && (() => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
221
|
+
/* ── Loading state ─────────────────────────────────────────── */
|
|
222
|
+
if (loading) {
|
|
223
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto p-6 space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold text-white tracking-wider", children: "Lending" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400", children: "MCC collateralized lending protocol" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-center py-20", children: [(0, jsx_runtime_1.jsx)(IconLoader, { className: "w-6 h-6 animate-spin text-neutral-400 mr-3" }), (0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Loading lending data..." })] })] }));
|
|
224
|
+
}
|
|
225
|
+
/* ── Error state ───────────────────────────────────────────── */
|
|
226
|
+
if (!poolData) {
|
|
227
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto p-6 space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold text-white tracking-wider", children: "Lending" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400", children: "MCC collateralized lending protocol" })] }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg p-6", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-3", children: [(0, jsx_runtime_1.jsx)(IconAlertTriangle, { className: "w-5 h-5 text-red-500 mt-0.5" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-red-500 font-medium", children: "Failed to load lending pool data" }), (0, jsx_runtime_1.jsx)("p", { className: "text-neutral-500 text-sm mt-1", children: "Please check your network connection and try again." })] })] }) }), (0, jsx_runtime_1.jsx)("button", { onClick: handleRefresh, className: "flex items-center gap-2 px-3 py-1.5 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 bg-transparent", children: "Retry" })] }));
|
|
228
|
+
}
|
|
229
|
+
/* ── Main render ───────────────────────────────────────────── */
|
|
230
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "max-w-7xl mx-auto p-6 space-y-6 font-mono", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-2xl font-bold text-white tracking-wider", children: "Lending" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400", children: "MCC collateralized lending protocol" })] }), (0, jsx_runtime_1.jsxs)("button", { onClick: handleRefresh, disabled: refreshing, className: "flex items-center gap-2 px-3 py-1.5 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 disabled:opacity-50 bg-transparent", children: [(0, jsx_runtime_1.jsx)(IconRefreshCw, { className: cn("w-4 h-4", refreshing && "animate-spin") }), "Refresh"] })] }), actionError && ((0, jsx_runtime_1.jsxs)("div", { className: "p-3 bg-red-500/10 border border-red-500/30 rounded text-red-400 text-sm flex items-start gap-2", children: [(0, jsx_runtime_1.jsx)(IconAlertTriangle, { className: "w-4 h-4 mt-0.5 shrink-0" }), actionError] })), actionSuccess && ((0, jsx_runtime_1.jsxs)("div", { className: "p-3 bg-cyan-400/10 border border-cyan-400/30 rounded text-cyan-400 text-sm flex items-start gap-2", children: [(0, jsx_runtime_1.jsx)(IconCheckCircle, { className: "w-4 h-4 mt-0.5 shrink-0" }), actionSuccess] })), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg hover:border-cyan-400/50 transition-colors", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-4 mb-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-cyan-400/20 rounded-xl border border-cyan-400/30", children: (0, jsx_runtime_1.jsx)(IconLandmark, { className: "h-8 w-8 text-white" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "pool_name" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white", children: "MCC Lending Pool" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mb-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconPiggyBank, { className: "h-4 w-4 text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "total_deposits" })] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xl font-bold text-white font-mono", children: fmtMCC(poolData.total_deposits ?? 0) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "MCC" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconCreditCard, { className: "h-4 w-4 text-cyan-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "total_borrowed" })] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xl font-bold text-cyan-400 font-mono", children: fmtMCC(poolData.total_borrows ?? 0) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "MCC" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconTrendingUp, { className: "h-4 w-4 text-white" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "deposit_apr" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xl font-bold text-white font-mono", children: [(poolData.supply_apr_percent ?? 0).toFixed(2), "%"] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "annualized" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)(IconTrendingUp, { className: "h-4 w-4 text-cyan-400" }), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "borrow_apr" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xl font-bold text-cyan-400 font-mono", children: [(poolData.borrow_apr_percent ?? 0).toFixed(2), "%"] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "annualized" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "utilization_rate" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-sm font-mono text-white", children: [(poolData.utilization_rate_percent ?? 0).toFixed(1), "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "w-full bg-neutral-800 rounded-full h-2", children: (0, jsx_runtime_1.jsx)("div", { className: "bg-cyan-400 h-2 rounded-full transition-all", style: { width: `${Math.min(poolData.utilization_rate_percent ?? 0, 100)}%` } }) })] })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-neutral-400 text-sm", children: [(0, jsx_runtime_1.jsx)(IconWallet, { className: "w-4 h-4" }), (0, jsx_runtime_1.jsx)("span", { className: "tracking-wider", children: "MY_DEPOSITS" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex gap-2", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => { setDepositOpen(true); setActionError(null); }, className: "flex items-center gap-1 px-3 py-1.5 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded", children: [(0, jsx_runtime_1.jsx)(IconPlus, { className: "w-4 h-4" }), "Deposit"] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => { setWithdrawOpen(true); setActionError(null); }, disabled: !lpData || (lpData.lp_balance ?? 0) <= 0, className: "flex items-center gap-1 px-3 py-1.5 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 disabled:opacity-50 bg-transparent", children: [(0, jsx_runtime_1.jsx)(IconMinus, { className: "w-4 h-4" }), "Withdraw"] })] })] }), lpData && (lpData.lp_balance ?? 0) > 0 ? ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "lp_token_balance" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white font-mono", children: fmtMCC(lpData.lp_balance ?? 0) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "LP Token" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "deposit_value" }), (0, jsx_runtime_1.jsx)("div", { className: "text-2xl font-bold text-white font-mono", children: fmtMCC(lpData.lp_value_in_mcc ?? lpData.lp_balance ?? 0) }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "MCC" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider mb-1", children: "earned_interest" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-2xl font-bold text-cyan-400 font-mono", children: ["+", fmtMCC(Math.max(0, (lpData.lp_value_in_mcc ?? lpData.lp_balance ?? 0) - (lpData.lp_balance ?? 0)))] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 mt-1", children: "MCC" })] })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "text-center py-8 text-neutral-500", children: [(0, jsx_runtime_1.jsx)(IconWallet, { className: "w-12 h-12 mx-auto mb-2 opacity-50" }), (0, jsx_runtime_1.jsx)("p", { children: "No deposits yet" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm mt-1", children: "Deposit MCC to earn interest from the lending pool" })] }))] }) }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-neutral-400 text-sm", children: [(0, jsx_runtime_1.jsx)(IconCreditCard, { className: "w-4 h-4" }), (0, jsx_runtime_1.jsx)("span", { className: "tracking-wider", children: "MY_LOANS" })] }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => { setBorrowOpen(true); setActionError(null); }, disabled: availableNFTs.length === 0, className: "flex items-center gap-1 px-3 py-1.5 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded disabled:opacity-50", children: [(0, jsx_runtime_1.jsx)(IconPlus, { className: "w-4 h-4" }), "New Loan"] })] }), userLoans.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "space-y-4", children: userLoans.map((loan) => {
|
|
231
|
+
const nftMintShort = `${loan.collateral_nft_mint.slice(0, 6)}...${loan.collateral_nft_mint.slice(-4)}`;
|
|
232
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700 hover:border-cyan-400/50 transition-colors", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-start mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "p-2 bg-cyan-400/20 rounded border border-cyan-400/30", children: (0, jsx_runtime_1.jsx)(IconImage, { className: "h-6 w-6 text-white" }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "font-bold text-white", children: [loan.collateral_nft_type, " #", loan.loan_id] }), (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-500 font-mono", children: nftMintShort })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [loan.status === 'Active' && ((0, jsx_runtime_1.jsx)("span", { className: cn("text-xs px-2 py-0.5 rounded border", loan.missed_payments > 0
|
|
233
|
+
? "bg-cyan-400/20 text-cyan-400 border-cyan-400/30"
|
|
234
|
+
: "bg-white/20 text-white border-white/30"), children: loan.missed_payments > 0 ? `${loan.missed_payments} missed` : 'Normal' })), loan.status === 'Repaid' && ((0, jsx_runtime_1.jsx)("span", { className: "text-xs px-2 py-0.5 rounded border bg-white/20 text-white border-white/30", children: "Repaid" })), loan.status === 'Liquidated' && ((0, jsx_runtime_1.jsx)("span", { className: "text-xs px-2 py-0.5 rounded border bg-red-500/20 text-red-500 border-red-500/30", children: "Liquidated" }))] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider", children: "collateral_value" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-lg font-bold text-white font-mono", children: [fmtMCC(loan.collateral_value), " MCC"] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider", children: "principal" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-lg font-bold text-cyan-400 font-mono", children: [fmtMCC(loan.principal), " MCC"] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider", children: "interest" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-lg font-bold text-cyan-400 font-mono", children: ["+", fmtMCC(loan.interest_accrued), " MCC"] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-neutral-400 tracking-wider", children: "total_debt" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-lg font-bold text-red-500 font-mono", children: [fmtMCC(loan.total_owed), " MCC"] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mb-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-xs text-neutral-400 tracking-wider", children: "LTV (Loan-to-Value)" }), (0, jsx_runtime_1.jsxs)("span", { className: cn("text-sm font-mono", loan.ltv >= 100 ? "text-red-500" : loan.ltv >= 80 ? "text-cyan-400" : "text-white"), children: [loan.ltv.toFixed(1), "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "w-full bg-neutral-800 rounded-full h-2", children: (0, jsx_runtime_1.jsx)("div", { className: cn("h-2 rounded-full transition-all", loan.ltv >= 100 ? "bg-red-500" : loan.ltv >= 80 ? "bg-cyan-400" : "bg-white"), style: { width: `${Math.min(loan.ltv, 100)}%` } }) })] }), loan.status === 'Active' && ((0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-neutral-500 flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)(IconClock, { className: "w-3 h-3 inline" }), "Rate: ", loan.borrow_rate.toFixed(2), "% APR | Monthly repayment"] }), (0, jsx_runtime_1.jsx)("button", { onClick: () => {
|
|
235
|
+
setSelectedLoan(loan);
|
|
236
|
+
setRepayOpen(true);
|
|
237
|
+
setActionError(null);
|
|
238
|
+
}, className: "px-3 py-1.5 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded", children: "Repay" })] })), loan.missed_payments >= 2 && loan.status === 'Active' && ((0, jsx_runtime_1.jsx)("div", { className: "mt-4 p-3 bg-red-500/10 border border-red-500/30 rounded", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-2", children: [(0, jsx_runtime_1.jsx)(IconAlertTriangle, { className: "h-4 w-4 text-red-500 mt-0.5" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-sm text-red-500", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium", children: "Liquidation Warning" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-red-500/70", children: [loan.missed_payments, " consecutive missed payments. Your NFT may be liquidated."] })] })] }) }))] }, loan.loan_id));
|
|
239
|
+
}) })) : ((0, jsx_runtime_1.jsxs)("div", { className: "text-center py-8 text-neutral-500", children: [(0, jsx_runtime_1.jsx)(IconCreditCard, { className: "w-12 h-12 mx-auto mb-2 opacity-50" }), (0, jsx_runtime_1.jsx)("p", { children: "No active loans" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm mt-1", children: "Pledge Territory NFT to borrow MCC from the pool" })] }))] }) }), (0, jsx_runtime_1.jsx)("div", { className: "bg-neutral-900 border border-neutral-700 rounded-lg", children: (0, jsx_runtime_1.jsxs)("div", { className: "p-6", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 text-neutral-400 text-sm mb-4", children: [(0, jsx_runtime_1.jsx)(IconInfo, { className: "w-4 h-4" }), (0, jsx_runtime_1.jsx)("span", { className: "tracking-wider", children: "PROTOCOL_INFO" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4 text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-white mb-2", children: "Loan Rules" }), (0, jsx_runtime_1.jsxs)("ul", { className: "list-disc list-inside space-y-1 text-neutral-400", children: [(0, jsx_runtime_1.jsx)("li", { children: "Collateral: Territory NFT (Station/Matrix/Sector/System)" }), (0, jsx_runtime_1.jsx)("li", { children: "Max LTV: 100% of NFT valuation" }), (0, jsx_runtime_1.jsx)("li", { children: "Interest: variable rate based on pool utilization" }), (0, jsx_runtime_1.jsx)("li", { children: "Duration: 3 / 7 / 30 days with auto-renewal" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-white mb-2", children: "Liquidation Rules" }), (0, jsx_runtime_1.jsxs)("ul", { className: "list-disc list-inside space-y-1 text-neutral-400", children: [(0, jsx_runtime_1.jsx)("li", { children: "LTV exceeds 90%: liquidation warning" }), (0, jsx_runtime_1.jsx)("li", { children: "Loan overdue or 2+ missed payments: liquidation eligible" }), (0, jsx_runtime_1.jsx)("li", { children: "Collateral NFT seized upon liquidation" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-white mb-2", children: "NFT Valuation" }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mt-2", children: Object.entries(COLLATERAL_TYPES).map(([key, val]) => ((0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-neutral-400 text-xs", children: val.description }), (0, jsx_runtime_1.jsx)("div", { className: "text-white font-bold font-mono", children: fmt(val.value) }), (0, jsx_runtime_1.jsx)("div", { className: "text-neutral-500 text-xs", children: "MCC" })] }, key))) })] })] })] }) }), (0, jsx_runtime_1.jsx)(Modal, { open: depositOpen, onClose: () => setDepositOpen(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-bold text-white", children: "Deposit MCC" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400 mt-1", children: "Earn interest by providing liquidity to the lending pool" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-cyan-400/10 border border-cyan-400/30 rounded", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-2", children: [(0, jsx_runtime_1.jsx)(IconInfo, { className: "h-4 w-4 text-cyan-400 mt-0.5 shrink-0" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-sm", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-cyan-400 mb-1", children: "Earnings Info" }), (0, jsx_runtime_1.jsxs)("ul", { className: "list-disc list-inside space-y-1 text-xs text-cyan-400/70", children: [(0, jsx_runtime_1.jsxs)("li", { children: ["Current APR: ", (poolData?.supply_apr_percent ?? 0).toFixed(2), "%"] }), (0, jsx_runtime_1.jsx)("li", { children: "Interest accrues in real-time" }), (0, jsx_runtime_1.jsx)("li", { children: "Withdraw anytime" })] })] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-neutral-400 text-xs tracking-wider", children: "amount (MCC)" }), (0, jsx_runtime_1.jsx)("input", { type: "number", step: "0.01", value: depositAmount, onChange: e => setDepositAmount(e.target.value), placeholder: "Enter deposit amount", className: "w-full px-3 py-2 bg-neutral-800 border border-neutral-600 rounded text-white placeholder-neutral-400 outline-none focus:border-cyan-400" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-3 pt-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setDepositOpen(false), className: "px-4 py-2 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 bg-transparent", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleDeposit, disabled: isSubmitting || !depositAmount, className: "px-4 py-2 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded disabled:opacity-50", children: isSubmitting ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(IconLoader, { className: "h-4 w-4 animate-spin" }), "Depositing..."] })) : ('Confirm Deposit') })] })] }) }), (0, jsx_runtime_1.jsx)(Modal, { open: withdrawOpen, onClose: () => setWithdrawOpen(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-bold text-white", children: "Withdraw MCC" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400 mt-1", children: "Withdraw deposited MCC from the lending pool" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Withdrawable Amount" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white font-bold font-mono", children: [fmtMCC(lpData?.lp_value_in_mcc ?? lpData?.lp_balance ?? 0), " MCC"] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-neutral-400 text-xs tracking-wider", children: "amount (MCC)" }), (0, jsx_runtime_1.jsx)("input", { type: "number", step: "0.01", value: withdrawAmount, onChange: e => setWithdrawAmount(e.target.value), placeholder: "Enter withdraw amount", className: "w-full px-3 py-2 bg-neutral-800 border border-neutral-600 rounded text-white placeholder-neutral-400 outline-none focus:border-cyan-400" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-3 pt-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setWithdrawOpen(false), className: "px-4 py-2 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 bg-transparent", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleWithdraw, disabled: isSubmitting || !withdrawAmount, className: "px-4 py-2 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded disabled:opacity-50", children: isSubmitting ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(IconLoader, { className: "h-4 w-4 animate-spin" }), "Withdrawing..."] })) : ('Confirm Withdraw') })] })] }) }), (0, jsx_runtime_1.jsx)(Modal, { open: borrowOpen, onClose: () => setBorrowOpen(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-bold text-white", children: "New Loan" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400 mt-1", children: "Borrow MCC using Territory NFT as collateral" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-neutral-400 text-xs tracking-wider", children: "select_nft" }), (0, jsx_runtime_1.jsxs)("select", { value: selectedNFT, onChange: e => setSelectedNFT(e.target.value), className: "w-full px-3 py-2 bg-neutral-800 border border-neutral-600 rounded text-white outline-none focus:border-cyan-400", children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Select Territory NFT..." }), availableNFTs.map((nft) => {
|
|
240
|
+
const nftType = NFT_TYPE_MAP[nft.nft_type] || 'Station';
|
|
241
|
+
return ((0, jsx_runtime_1.jsxs)("option", { value: nft.mint, children: [nft.name, " (", COLLATERAL_TYPES[nftType].description, ")"] }, nft.mint));
|
|
242
|
+
})] })] }), selectedNFT && (() => {
|
|
243
|
+
const selectedNftData = availableNFTs.find((n) => n.mint === selectedNFT);
|
|
244
|
+
const nftType = NFT_TYPE_MAP[selectedNftData?.nft_type || 'Station'] || 'Station';
|
|
245
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Collateral Value" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white font-bold font-mono", children: [fmt(COLLATERAL_TYPES[nftType].value), " MCC"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Max Borrow" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white font-bold font-mono", children: [fmt(COLLATERAL_TYPES[nftType].value), " MCC"] })] })] }));
|
|
246
|
+
})(), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-neutral-400 text-xs tracking-wider", children: "borrow_amount (MCC)" }), (0, jsx_runtime_1.jsx)("input", { type: "number", step: "0.01", value: borrowAmount, onChange: e => setBorrowAmount(e.target.value), placeholder: "Enter borrow amount", className: "w-full px-3 py-2 bg-neutral-800 border border-neutral-600 rounded text-white placeholder-neutral-400 outline-none focus:border-cyan-400" })] }), (0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-cyan-400/10 border border-cyan-400/30 rounded", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-2", children: [(0, jsx_runtime_1.jsx)(IconAlertTriangle, { className: "h-4 w-4 text-cyan-400 mt-0.5 shrink-0" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-sm", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-cyan-400 mb-1", children: "Borrow Notice" }), (0, jsx_runtime_1.jsxs)("ul", { className: "list-disc list-inside space-y-1 text-xs text-cyan-400/70", children: [(0, jsx_runtime_1.jsx)("li", { children: "NFT will be locked as collateral" }), (0, jsx_runtime_1.jsxs)("li", { children: ["Current borrow APR: ", (poolData?.borrow_apr_percent ?? 0).toFixed(2), "%"] }), (0, jsx_runtime_1.jsx)("li", { children: "Failure to repay may result in NFT liquidation" })] })] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-3 pt-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => setBorrowOpen(false), className: "px-4 py-2 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 bg-transparent", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleBorrow, disabled: isSubmitting || !selectedNFT || !borrowAmount, className: "px-4 py-2 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded disabled:opacity-50", children: isSubmitting ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(IconLoader, { className: "h-4 w-4 animate-spin" }), "Processing..."] })) : ('Confirm Borrow') })] })] }) }), (0, jsx_runtime_1.jsx)(Modal, { open: repayOpen, onClose: () => { setRepayOpen(false); setSelectedLoan(null); setRepayAmount(''); }, children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-bold text-white", children: "Repay Loan" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-neutral-400 mt-1", children: selectedLoan
|
|
247
|
+
? `Repay loan for ${selectedLoan.collateral_nft_type} #${selectedLoan.loan_id}`
|
|
248
|
+
: 'Repay borrowed MCC to release collateral' })] }), selectedLoan && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-neutral-800 rounded border border-neutral-700", children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Principal" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-white font-mono", children: [fmtMCC(selectedLoan.principal), " MCC"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-400", children: "Interest" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-cyan-400 font-mono", children: ["+", fmtMCC(selectedLoan.interest_accrued), " MCC"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "border-t border-neutral-700 pt-2 flex justify-between items-center font-bold", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-neutral-300", children: "Total Debt" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-red-500 font-mono", children: [fmtMCC(selectedLoan.total_owed), " MCC"] })] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-neutral-400 text-xs tracking-wider", children: "repay_amount (MCC)" }), (0, jsx_runtime_1.jsx)("input", { type: "number", step: "0.01", value: repayAmount, onChange: e => setRepayAmount(e.target.value), placeholder: "Enter repay amount", className: "w-full px-3 py-2 bg-neutral-800 border border-neutral-600 rounded text-white placeholder-neutral-400 outline-none focus:border-cyan-400" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setRepayAmount((selectedLoan.total_owed / 1000000000).toString()), className: "text-xs text-cyan-400 hover:text-cyan-300", children: "Repay full amount" })] })] })), (0, jsx_runtime_1.jsx)("div", { className: "p-4 bg-cyan-400/10 border border-cyan-400/30 rounded", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-2", children: [(0, jsx_runtime_1.jsx)(IconCheckCircle, { className: "h-4 w-4 text-cyan-400 mt-0.5 shrink-0" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-sm", children: [(0, jsx_runtime_1.jsx)("div", { className: "font-medium text-cyan-400 mb-1", children: "After Full Repay" }), (0, jsx_runtime_1.jsxs)("ul", { className: "list-disc list-inside space-y-1 text-xs text-cyan-400/70", children: [(0, jsx_runtime_1.jsx)("li", { children: "Collateral NFT returned to your wallet" }), (0, jsx_runtime_1.jsx)("li", { children: "Loan record marked as repaid" })] })] })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end gap-3 pt-2", children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => { setRepayOpen(false); setSelectedLoan(null); setRepayAmount(''); }, className: "px-4 py-2 text-sm border border-neutral-700 rounded text-neutral-400 hover:bg-neutral-800 hover:text-neutral-300 bg-transparent", children: "Cancel" }), (0, jsx_runtime_1.jsx)("button", { onClick: handleRepay, disabled: isSubmitting || !repayAmount, className: "px-4 py-2 text-sm bg-cyan-700 hover:bg-cyan-600 text-white rounded disabled:opacity-50", children: isSubmitting ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)(IconLoader, { className: "h-4 w-4 animate-spin" }), "Repaying..."] })) : ('Confirm Repay') })] })] }) })] }));
|
|
147
249
|
}
|