-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTradeHistory.jsx
More file actions
157 lines (147 loc) · 6.78 KB
/
Copy pathTradeHistory.jsx
File metadata and controls
157 lines (147 loc) · 6.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
* DexScreener-style transactions table for a token's LaunchPool: every
* on-chain Swap in the recent block window, newest first, with buy/sell
* filter tabs. Rows link the maker and the transaction to the explorer.
*/
import { useState } from 'react';
import Card from './Card.jsx';
import { blocksToApproxTime, fmtAmount, fmtNative, shortAddr } from '../data/launches.js';
import { EXPLORER_URL, RITUAL_TESTNET } from '../config/ritual.js';
const SYM = RITUAL_TESTNET.nativeCurrency.symbol;
const PAGE_SIZE = 10;
const FILTERS = ['all', 'buys', 'sells'];
const thClass = 'px-3 py-3 font-medium first:pl-6 last:pr-6';
export default function TradeHistory({ trades, symbol, currentBlock }) {
const [filter, setFilter] = useState('all');
const [page, setPage] = useState(0);
const buys = trades.filter((t) => t.isBuy).length;
const counts = { all: trades.length, buys, sells: trades.length - buys };
const filtered = [...trades]
.reverse()
.filter((t) => filter === 'all' || t.isBuy === (filter === 'buys'));
const pageCount = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE));
const current = Math.min(page, pageCount - 1);
const rows = filtered.slice(current * PAGE_SIZE, (current + 1) * PAGE_SIZE);
function switchFilter(next) {
setFilter(next);
setPage(0);
}
return (
<Card className="mt-6 overflow-hidden">
<div className="flex flex-wrap items-center justify-between gap-3 border-b border-linefaint px-6 py-4">
<h2 className="font-display text-lg font-medium text-cream">Transactions</h2>
<div className="flex rounded-lg border border-line p-0.5">
{FILTERS.map((f) => (
<button
key={f}
type="button"
onClick={() => switchFilter(f)}
className={`rounded-md px-3 py-1 text-xs font-semibold capitalize transition-colors ${
filter === f ? 'bg-ember/20 text-cream' : 'text-faint hover:text-fog'
}`}
>
{f} <span className={filter === f ? 'text-fog' : ''}>{counts[f]}</span>
</button>
))}
</div>
</div>
{rows.length === 0 ? (
<p className="px-6 py-8 text-sm text-faint">
{trades.length === 0
? 'No swaps in the recent block window yet — trades appear here the moment they settle.'
: `No ${filter} in the recent block window.`}
</p>
) : (
<div className="overflow-x-auto">
<table className="w-full min-w-160 text-sm">
<thead>
<tr className="border-b border-linefaint text-left text-[11px] uppercase tracking-wider text-faint">
<th className={thClass}>Age</th>
<th className={thClass}>Type</th>
<th className={`${thClass} text-right`}>Price ({SYM})</th>
<th className={`${thClass} text-right`}>Amount ({symbol})</th>
<th className={`${thClass} text-right`}>Total ({SYM})</th>
<th className={`${thClass} text-right`}>Maker</th>
<th className={`${thClass} text-right`}>Txn</th>
</tr>
</thead>
<tbody className="divide-y divide-linefaint">
{rows.map((t) => {
const tone = t.isBuy ? 'text-status-good' : 'text-status-warn';
return (
<tr key={`${t.txHash}-${t.logIndex}`} className="transition-colors hover:bg-ink/60">
<td className="whitespace-nowrap py-2.5 pl-6 pr-3 text-faint">
{blocksToApproxTime(currentBlock - t.block)}
</td>
<td className={`px-3 py-2.5 font-semibold ${tone}`}>{t.isBuy ? 'Buy' : 'Sell'}</td>
<td className={`mono px-3 py-2.5 text-right ${tone}`}>{fmtNative(t.price)}</td>
<td className={`mono px-3 py-2.5 text-right ${tone}`}>{fmtAmount(t.tokens)}</td>
<td className={`mono px-3 py-2.5 text-right ${tone}`}>{fmtNative(t.native)}</td>
<td className="mono whitespace-nowrap px-3 py-2.5 text-right">
<a
href={`${EXPLORER_URL}/address/${t.maker}`}
target="_blank"
rel="noreferrer"
title={t.maker}
className="text-fog underline decoration-ember/30 underline-offset-4 transition-colors hover:text-gold"
>
{shortAddr(t.maker)}
</a>
</td>
<td className="py-2.5 pl-3 pr-6 text-right">
<a
href={`${EXPLORER_URL}/tx/${t.txHash}`}
target="_blank"
rel="noreferrer"
aria-label="View transaction on explorer"
className="inline-flex text-faint transition-colors hover:text-gold"
>
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<path
d="M6.5 3.5h6v6m0-6L5 11m-1.5-6H3A1.5 1.5 0 0 0 1.5 6.5V13A1.5 1.5 0 0 0 3 14.5h6.5A1.5 1.5 0 0 0 11 13v-.5"
stroke="currentColor"
strokeWidth="1.4"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</a>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
)}
<div className="flex flex-wrap items-center justify-between gap-3 border-t border-linefaint px-6 py-3">
<span className="text-xs text-faint">
Read live from the pool's on-chain Swap events — ages are block-derived approximations.
</span>
{pageCount > 1 && (
<div className="flex items-center gap-3 text-xs">
<button
type="button"
disabled={current === 0}
onClick={() => setPage(current - 1)}
className="font-semibold text-gold transition-colors hover:text-cream disabled:cursor-not-allowed disabled:text-faint"
>
‹ Prev
</button>
<span className="text-faint">
Page {current + 1} of {pageCount}
</span>
<button
type="button"
disabled={current >= pageCount - 1}
onClick={() => setPage(current + 1)}
className="font-semibold text-gold transition-colors hover:text-cream disabled:cursor-not-allowed disabled:text-faint"
>
Next ›
</button>
</div>
)}
</div>
</Card>
);
}