Skip to content

Commit c7ca3e8

Browse files
committed
added skeleton in loading states
1 parent 1158977 commit c7ca3e8

File tree

9 files changed

+278
-95
lines changed

9 files changed

+278
-95
lines changed

app/dashboard/page.tsx

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { DashboardOverview } from "@/components/dashboard/dashboard-overview";
44
import { PoolsTable } from "@/components/dashboard/pools-table";
55
import { RecentActivity } from "@/components/dashboard/recent-activity";
6+
import { DashboardSkeleton } from "@/components/dashboard/dashboard-skeleton";
67
import { useAccount, usePublicClient, useWriteContract } from "wagmi";
78
import { ContractClient } from "@/lib/contract-client";
89
import { CONTRACT_ADDRESS } from "@/types/contract";
@@ -44,16 +45,18 @@ export default function DashboardPage() {
4445

4546
const fetchUserPools = async (poolCount: number) => {
4647
if (!address) return;
47-
if(poolCount == pools.length) return;
48+
if (poolCount === 0) return;
4849
try {
50+
const allPools: RowPool[] = [];
4951
for (let i = 0; i < Math.ceil(poolCount / POOL_FETCH_LIMIT); i++) {
5052
const userPools = await contractClient.getUserPools(
5153
address,
5254
i * POOL_FETCH_LIMIT,
53-
Math.min(POOL_FETCH_LIMIT,poolCount - 1)
55+
Math.min(i * POOL_FETCH_LIMIT + POOL_FETCH_LIMIT, poolCount - 1)
5456
);
55-
setPools((prevPools) => [...prevPools, ...userPools]);
57+
allPools.push(...userPools);
5658
}
59+
setPools(allPools);
5760
} catch (error) {
5861
console.error("Error fetching user pools:", error);
5962
toast.error("Failed to fetch user pools.");
@@ -96,44 +99,42 @@ export default function DashboardPage() {
9699
}
97100
};
98101

99-
const fetchUserLiquidity = async () => {
100-
if (!address) return;
101-
try {
102-
const liquidity = await contractClient.getUserLiquidity(address);
103-
setTotalLiquidity(formatEther(BigInt(liquidity)));
104-
} catch (error) {
105-
console.error("Error fetching user liquidity:", error);
106-
toast.error("Failed to fetch user liquidity.");
107-
}
108-
};
109102

110-
const calculatePortfolioValue = () => {
103+
104+
const calculatePortfolioValue = (poolsData: RowPool[]) => {
111105
let total = 0;
112-
for (const pool of pools) {
106+
for (const pool of poolsData) {
113107
const proportion =
114-
BigInt(pool.lpToken!.balance) / BigInt(pool.lpToken!.totalSupply);
108+
Number(pool.lpToken!.balance) / Number(pool.lpToken!.totalSupply);
115109
total += Number(proportion) * Number(pool.totalLiquidity);
116110
}
117-
console.log(pools)
118-
setPortfolioValue((formatEther(BigInt(total))));
111+
return formatEther(BigInt(total));
119112
};
120113

121-
const calculateProfit = () => {
122-
const gain =
123-
((Number(portfolioValue) - Number(totalLiquidity)) /
124-
Number(totalLiquidity)) *
125-
100;
126-
setTotalProfit(gain.toFixed(2));
114+
const calculateProfit = (portfolio: string, liquidity: string) => {
115+
const portfolioNum = Number(portfolio);
116+
const liquidityNum = Number(liquidity);
117+
if (liquidityNum === 0) return "0.00";
118+
const gain = ((portfolioNum - liquidityNum) / liquidityNum) * 100;
119+
return gain.toFixed(2);
127120
};
128121

129122
useEffect(() => {
123+
if (!address) return;
124+
130125
const fetchData = async () => {
131126
try {
132127
setIsLoading(true);
128+
129+
// Fetch all data
133130
const count = await fetchPoolCount();
134-
if(count == undefined) throw new Error("Pool count is undefined");
131+
if (count === undefined) throw new Error("Pool count is undefined");
132+
135133
await fetchUserPools(count);
136-
await fetchUserLiquidity();
134+
const liquidity = await contractClient.getUserLiquidity(address);
135+
const liquidityFormatted = formatEther(BigInt(liquidity));
136+
setTotalLiquidity(liquidityFormatted);
137+
137138
await fetchRecentDeposits();
138139
await fetchRecentWithdrawals();
139140

@@ -144,14 +145,23 @@ export default function DashboardPage() {
144145
setIsLoading(false);
145146
}
146147
};
147-
const calculateValues = () => {
148-
calculatePortfolioValue();
149-
calculateProfit();
150-
setIsLoading(false);
151-
}
148+
152149
fetchData();
153-
calculateValues();
154-
},[pools]);
150+
}, [address]);
151+
152+
// Separate effect to calculate values when pools or liquidity changes
153+
useEffect(() => {
154+
if (pools.length > 0 && totalLiquidity !== "0") {
155+
const portfolio = calculatePortfolioValue(pools);
156+
setPortfolioValue(portfolio);
157+
const profit = calculateProfit(portfolio, totalLiquidity);
158+
setTotalProfit(profit);
159+
}
160+
}, [pools, totalLiquidity]);
161+
162+
if (isLoading) {
163+
return <DashboardSkeleton />;
164+
}
155165

156166
return (
157167
<div className="min-h-screen relative bg-gradient-pattern overflow-hidden">

app/globals.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@
252252
animation: slide-up 0.3s ease-out;
253253
}
254254

255+
.animate-shimmer {
256+
animation: shimmer 2s ease-in-out infinite;
257+
}
258+
255259
/* Startup-style liquid animations */
256260
.animate-liquid-primary {
257261
animation: liquid-flow-primary 35s ease-in-out infinite;
@@ -768,6 +772,15 @@
768772
}
769773
}
770774

775+
@keyframes shimmer {
776+
0% {
777+
transform: translateX(-100%);
778+
}
779+
100% {
780+
transform: translateX(100%);
781+
}
782+
}
783+
771784
/* Accessibility and reduced motion support */
772785
@media (prefers-reduced-motion: reduce) {
773786
.animate-ripple,
@@ -778,6 +791,7 @@
778791
.animate-pulse-slow,
779792
.animate-fade-in,
780793
.animate-slide-up,
794+
.animate-shimmer,
781795
.animate-liquid-primary,
782796
.animate-liquid-secondary {
783797
animation: none;

components/dashboard/dashboard-overview.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ export function DashboardOverview({
2020
const stats = [
2121
{
2222
label: "Portfolio Value",
23-
value: loading ? "..." : `${portfolioValue} ETH`,
23+
value: loading ? "..." : `${Number(portfolioValue).toFixed(8)} ETH`,
2424
},
2525
{
2626
label: "Total Liquidity",
27-
value: loading ? "..." : `${totalLiquidity} ETH`,
27+
value: loading ? "..." : `${Number(totalLiquidity).toFixed(8)} ETH`,
2828
},
2929
{
3030
label: "Active Pools",
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
export function DashboardSkeleton() {
2+
return (
3+
<div className="min-h-screen relative bg-gradient-pattern overflow-hidden">
4+
<main className="container relative mx-auto px-4 py-8">
5+
<div className="space-y-8">
6+
{/* Page Header Skeleton */}
7+
<div className="relative rounded-xl p-8 overflow-hidden backdrop-blur-xl border border-white/[0.05]">
8+
<div className="absolute inset-0 bg-gradient-to-br from-accent/[0.08] to-primary-500/[0.05]" />
9+
<div className="absolute inset-0 border border-white/[0.05] rounded-xl" />
10+
<div className="relative">
11+
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
12+
<div className="space-y-3">
13+
<div className="h-10 w-64 bg-white/5 rounded-lg animate-pulse" />
14+
<div className="h-4 w-96 bg-white/5 rounded animate-pulse" />
15+
</div>
16+
</div>
17+
</div>
18+
</div>
19+
20+
{/* Overview Cards Skeleton */}
21+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
22+
{Array.from({ length: 4 }).map((_, i) => (
23+
<div
24+
key={i}
25+
className="relative overflow-hidden border-0 rounded-lg"
26+
>
27+
<div className="absolute inset-0 bg-background-800/40 backdrop-blur-xl" />
28+
<div className="absolute inset-0 bg-gradient-to-br from-accent/[0.08] to-primary-500/[0.05]" />
29+
<div className="absolute inset-0 border border-white/[0.05] rounded-lg" />
30+
<div className="relative z-10 p-6 space-y-4">
31+
<div className="h-8 w-32 bg-white/5 rounded-lg animate-pulse" />
32+
<div className="h-4 w-24 bg-white/5 rounded animate-pulse" />
33+
</div>
34+
{/* Shimmer effect */}
35+
<div className="absolute inset-0 -z-10">
36+
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent animate-shimmer" />
37+
</div>
38+
</div>
39+
))}
40+
</div>
41+
42+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
43+
{/* Left Column - Pools Table Skeleton */}
44+
<div className="lg:col-span-2 space-y-8">
45+
<div className="relative rounded-xl overflow-hidden backdrop-blur-xl border border-white/[0.05]">
46+
<div className="absolute inset-0 bg-gradient-to-br from-accent/[0.08] to-primary-500/[0.05]" />
47+
<div className="absolute inset-0 border border-white/[0.05] rounded-xl" />
48+
<div className="relative p-6">
49+
<div className="flex items-center justify-between mb-6">
50+
<div className="h-7 w-48 bg-white/5 rounded-lg animate-pulse" />
51+
<div className="h-5 w-24 bg-white/5 rounded animate-pulse" />
52+
</div>
53+
<div className="space-y-3">
54+
{Array.from({ length: 3 }).map((_, i) => (
55+
<div
56+
key={i}
57+
className="relative p-4 rounded-lg backdrop-blur-sm border border-white/[0.05]
58+
before:absolute before:inset-0 before:bg-background-800/30 before:-z-10"
59+
>
60+
<div className="relative flex items-center gap-4">
61+
{/* Logo Skeleton */}
62+
<div className="relative h-10 w-10">
63+
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-accent/10 to-primary-500/10 animate-pulse-slow" />
64+
<div className="absolute inset-0 rounded-full bg-white/5 animate-pulse backdrop-blur-sm" />
65+
</div>
66+
67+
{/* Token Info Skeleton */}
68+
<div className="flex-1 space-y-2">
69+
<div className="h-5 w-24 bg-white/5 rounded animate-pulse" />
70+
<div className="h-4 w-32 bg-white/5 rounded animate-pulse" />
71+
</div>
72+
73+
{/* Chart Skeleton */}
74+
<div className="hidden sm:block">
75+
<div className="h-12 w-32 bg-white/5 rounded animate-pulse" />
76+
</div>
77+
78+
{/* Liquidity Skeleton */}
79+
<div className="hidden lg:block space-y-2">
80+
<div className="h-4 w-24 bg-white/5 rounded animate-pulse" />
81+
<div className="h-3 w-16 bg-white/5 rounded animate-pulse" />
82+
</div>
83+
</div>
84+
85+
{/* Loading shimmer effect */}
86+
<div className="absolute inset-0 -z-20">
87+
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent animate-shimmer" />
88+
</div>
89+
</div>
90+
))}
91+
</div>
92+
</div>
93+
</div>
94+
</div>
95+
96+
{/* Right Column - Activity Skeleton */}
97+
<div className="relative rounded-xl overflow-hidden backdrop-blur-xl border border-white/[0.05]">
98+
<div className="absolute inset-0 bg-gradient-to-br from-accent/[0.08] to-primary-500/[0.05]" />
99+
<div className="absolute inset-0 border border-white/[0.05] rounded-xl" />
100+
<div className="relative p-6">
101+
<div className="h-7 w-40 bg-white/5 rounded-lg animate-pulse mb-6" />
102+
<div className="space-y-4">
103+
{Array.from({ length: 5 }).map((_, i) => (
104+
<div
105+
key={i}
106+
className="relative rounded-lg p-4 border border-white/[0.05]"
107+
>
108+
<div className="flex items-start gap-4">
109+
<div className="flex-1 space-y-3">
110+
<div className="flex items-center justify-between gap-2">
111+
<div className="h-4 w-20 bg-white/5 rounded animate-pulse" />
112+
<div className="h-3 w-16 bg-white/5 rounded animate-pulse" />
113+
</div>
114+
<div className="flex items-center gap-2">
115+
<div className="h-6 w-16 bg-white/5 rounded-full animate-pulse" />
116+
<div className="h-4 w-40 bg-white/5 rounded animate-pulse" />
117+
</div>
118+
</div>
119+
</div>
120+
{/* Shimmer effect */}
121+
<div className="absolute inset-0 -z-10">
122+
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent animate-shimmer" />
123+
</div>
124+
</div>
125+
))}
126+
</div>
127+
</div>
128+
</div>
129+
</div>
130+
</div>
131+
</main>
132+
133+
{/* Dynamic glow effects */}
134+
<div className="fixed inset-0 pointer-events-none">
135+
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,var(--accent-cyan)/10%,transparent_50%)]" />
136+
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_bottom,var(--primary-500)/5%,transparent_50%)]" />
137+
</div>
138+
</div>
139+
);
140+
}

0 commit comments

Comments
 (0)