Skip to content

Commit 06e89d8

Browse files
committed
feat: added all the dune stats
1 parent 625fa58 commit 06e89d8

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

packages/client/src/pages/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export default function Page() {
120120
const observer = useRef<IntersectionObserver | null>(null);
121121
const lastElementRef = useCallback(
122122
(node: HTMLDivElement) => {
123+
console.log("Last element ref called");
123124
if (query.isLoading || query.isFetchingNextPage) return;
124125
// Disconnect previous observer if any
125126
if (observer.current) observer.current.disconnect();

packages/client/src/pages/stats.tsx

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
import useAuthentication from "@/hooks/use-authentication";
2+
import { useTokens, UseTokensParams } from "@/hooks/use-tokens";
3+
import { IToken } from "@/types";
4+
import {
5+
abbreviateNumber,
6+
formatNumber,
7+
formatNumberSubscriptSmart,
8+
} from "@/utils";
9+
import { env } from "@/utils/env";
10+
111
const GridViewStats = ({
212
title,
313
iframes,
@@ -22,7 +32,126 @@ const GridViewStats = ({
2232
</div>
2333
);
2434
};
35+
36+
import { useCallback, useRef } from "react";
37+
import { Link } from "react-router";
38+
39+
const BondedTokenRow = ({ token }: { token: IToken }) => {
40+
const priceChangeColor =
41+
token.priceChange24h && token.priceChange24h > 0
42+
? "text-green-500"
43+
: token.priceChange24h && token.priceChange24h < 0
44+
? "text-red-500"
45+
: "text-gray-400";
46+
47+
return (
48+
<Link
49+
to={`/token/${token.mint}`}
50+
className="flex flex-col sm:flex-row items-center justify-between p-4 border-b border-gray-700 hover:bg-gray-800 transition-colors duration-150"
51+
>
52+
<div className="flex items-center mb-4 sm:mb-0 sm:w-1/3 lg:w-1/4">
53+
<img
54+
src={token.image || "/user-placeholder.png"}
55+
alt={token.name || "Token"}
56+
className="w-10 h-10 rounded-full mr-3 object-cover"
57+
onError={(e) => (e.currentTarget.src = "/user-placeholder.png")}
58+
/>
59+
<div className="flex flex-col">
60+
<span
61+
className="font-semibold text-white truncate max-w-[150px] sm:max-w-[200px]"
62+
title={token.name || "N/A"}
63+
>
64+
{token.name || "N/A"}
65+
</span>
66+
<span className="text-xs text-gray-400 uppercase">
67+
{token.ticker || "N/A"}
68+
</span>
69+
</div>
70+
</div>
71+
72+
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-x-4 gap-y-2 text-sm w-full sm:w-2/3 lg:w-3/4 text-right">
73+
<div className="flex flex-col items-end">
74+
<span className="text-xs text-gray-400">Price</span>
75+
<span className="text-white font-medium">
76+
{formatNumberSubscriptSmart(token.tokenPriceUSD, 3)}
77+
</span>
78+
</div>
79+
<div className="flex flex-col items-end">
80+
<span className="text-xs text-gray-400">24h Change</span>
81+
<span className={`${priceChangeColor} font-medium`}>
82+
{token.priceChange24h?.toFixed(2) || "0.00"}%
83+
</span>
84+
</div>
85+
<div className="flex flex-col items-end">
86+
<span className="text-xs text-gray-400">Market Cap</span>
87+
<span className="text-white font-medium">
88+
{formatNumber(token.marketCapUSD)}
89+
</span>
90+
</div>
91+
<div className="flex flex-col items-end">
92+
<span className="text-xs text-gray-400">24h Volume</span>
93+
<span className="text-white font-medium">
94+
{formatNumber(token.volume24h)}
95+
</span>
96+
</div>
97+
<div className="flex flex-col items-end col-span-2 sm:col-span-1">
98+
<span className="text-xs text-gray-400">Holders</span>
99+
<span className="text-white font-medium">
100+
{abbreviateNumber(token.holderCount, true)}
101+
</span>
102+
</div>
103+
{/* Optional: Created At
104+
<div className="flex flex-col items-end">
105+
<span className="text-xs text-gray-400">Created</span>
106+
<span className="text-white font-medium">
107+
{token.createdAt ? new Date(token.createdAt).toLocaleDateString() : 'N/A'}
108+
</span>
109+
</div>
110+
*/}
111+
</div>
112+
</Link>
113+
);
114+
};
115+
25116
export default function StatsPage() {
117+
const { walletAddress } = useAuthentication();
118+
const isAdmin =
119+
(walletAddress && env.adminAddresses.includes(walletAddress)) || false;
120+
121+
const params: UseTokensParams = {
122+
hideImported: 1,
123+
sortBy: "createdAt",
124+
sortOrder: "desc",
125+
status: "locked",
126+
};
127+
128+
const query = useTokens(params);
129+
130+
const observer = useRef<IntersectionObserver | null>(null);
131+
const lastElementRef = useCallback(
132+
(node: HTMLDivElement) => {
133+
if (query.isLoading || query.isFetchingNextPage) return;
134+
if (observer.current) observer.current.disconnect();
135+
observer.current = new IntersectionObserver((entries) => {
136+
if (entries[0].isIntersecting && query.hasNextPage) {
137+
(query.fetchNextPage as any)();
138+
}
139+
});
140+
if (node) observer.current.observe(node);
141+
},
142+
[
143+
query.isLoading,
144+
query.isFetchingNextPage,
145+
query.hasNextPage,
146+
query.fetchNextPage,
147+
],
148+
);
149+
150+
if (!isAdmin) {
151+
window.location.href = "/";
152+
return null;
153+
}
154+
26155
return (
27156
<div className="mt-4 mx-4">
28157
<GridViewStats
@@ -47,6 +176,16 @@ export default function StatsPage() {
47176
"https://dune.com/embeds/5133685/8465708?darkMode=true",
48177
]}
49178
/>
179+
180+
<div className="flex flex-col items-center mt-12">
181+
<h1 className="text-3xl font-bold mb-6">Bonded Tokens</h1>
182+
<div className="max-w-[1600px]">
183+
{query.items.map((token) => (
184+
<BondedTokenRow key={token.mint} token={token} />
185+
))}
186+
</div>
187+
</div>
188+
<div ref={lastElementRef} className="h-10 w-full" />
50189
</div>
51190
);
52191
}

0 commit comments

Comments
 (0)