Skip to content

Commit 1c61e2f

Browse files
committed
fix
1 parent 8d922c5 commit 1c61e2f

File tree

7 files changed

+121
-100
lines changed

7 files changed

+121
-100
lines changed

app/page.tsx

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import {
33
getCoinDetails,
44
getCoinOHLC,
5+
getTopGainersLosers,
56
getTrendingCoins,
67
} from '@/lib/ coingecko.actions';
78
import { cn, formatPercentage, formatPrice } from '@/lib/utils';
@@ -20,9 +21,11 @@ import ChartSection from '@/components/ChartSection';
2021
// import CoinCard from '@/components/CoinCard';
2122
// import { popularCoins } from '@/lib/constants';
2223
import { TrendingDown, TrendingUp } from 'lucide-react';
24+
import CoinCard from '@/components/CoinCard';
2325

2426
const Home = async () => {
2527
const trendingCoins = await getTrendingCoins();
28+
const topGainersLosers = await getTopGainersLosers();
2629
const coinData = await getCoinDetails('bitcoin');
2730
const coinOHLCData = await getCoinOHLC(
2831
'bitcoin',
@@ -32,6 +35,8 @@ const Home = async () => {
3235
'full' // precision
3336
);
3437

38+
console.log('===topGainersLosers', topGainersLosers);
39+
3540
return (
3641
<main className='py-6 md:py-12 container size-full space-y-6 md:space-y-6'>
3742
<section className='grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 items-start lg:items-center gap-6'>
@@ -45,7 +50,7 @@ const Home = async () => {
4550
{/* Top Movers */}
4651
<div className='w-full flex flex-col justify-center h-full py-4 bg-dark-500 rounded-xl'>
4752
<h4 className='text-xl md:text-2xl px-4 md:px-5 mb-2'>Top Movers</h4>
48-
<div className='bg-dark-500 rounded-xl custom-scrollbar overflow-hidden'>
53+
<div className='bg-dark-500 custom-scrollbar overflow-hidden'>
4954
<Table>
5055
<TableHeader className='text-purple-100'>
5156
<TableRow className='hover:bg-transparent'>
@@ -76,7 +81,7 @@ const Home = async () => {
7681
<TableCell className='font-bold'>
7782
<Link
7883
href={`/coins/${item.id}`}
79-
className='pl-1 md:pl-2 py-1 flex items-center gap-2 md:gap-3'
84+
className='pl-1 md:pl-2 py-1 md:py-2 xl:py-1 flex items-center gap-2 md:gap-3'
8085
>
8186
<Image
8287
src={item.large}
@@ -123,17 +128,44 @@ const Home = async () => {
123128
</div>
124129
</section>
125130

126-
{/* <section className='w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6'>
127-
{popularCoins.map((coin) => (
128-
<CoinCard
129-
key={coin.coinId}
130-
coinId={coin.coinId}
131-
name={coin.name}
132-
symbol={coin.symbol}
133-
image={coin.image}
134-
/>
135-
))}
136-
</section> */}
131+
<section className='space-y-6 mt-5'>
132+
<h4 className='text-xl md:text-2xl'>Top Gainers</h4>
133+
<div className='w-full grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4 md:gap-6'>
134+
{topGainersLosers.top_gainers.map(
135+
(coin: TopGainersLosersResponse) => (
136+
<CoinCard
137+
key={coin.id}
138+
id={coin.id}
139+
name={coin.name}
140+
symbol={coin.symbol}
141+
image={coin.image}
142+
price={coin.usd}
143+
priceChangePercentage24h={coin.usd_24h_change}
144+
volume24={coin.usd_24h_vol}
145+
rank={coin.market_cap_rank}
146+
/>
147+
)
148+
)}
149+
</div>
150+
</section>
151+
<section className='space-y-6 mt-8'>
152+
<h4 className='text-xl md:text-2xl'>Top Losers</h4>
153+
<div className='w-full grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4 md:gap-6'>
154+
{topGainersLosers.top_losers.map((coin: TopGainersLosersResponse) => (
155+
<CoinCard
156+
key={coin.id}
157+
id={coin.id}
158+
name={coin.name}
159+
symbol={coin.symbol}
160+
image={coin.image}
161+
price={coin.usd}
162+
priceChangePercentage24h={coin.usd_24h_change}
163+
volume24={coin.usd_24h_vol}
164+
rank={coin.market_cap_rank}
165+
/>
166+
))}
167+
</div>
168+
</section>
137169
</main>
138170
);
139171
};

components/CoinCard.tsx

Lines changed: 35 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,25 @@
11
'use client';
22

3-
import { useCoinPrice } from '@/hooks/useCoinPrice';
43
import { formatPrice, formatPercentage, cn } from '@/lib/utils';
54
import Link from 'next/link';
65
import Image from 'next/image';
76
import { TrendingUp, TrendingDown } from 'lucide-react';
87
import { Badge } from './ui/badge';
98

109
export default function CoinCard({
11-
coinId,
10+
id,
1211
name,
1312
symbol,
1413
image,
15-
}: {
16-
coinId: string;
17-
name: string;
18-
symbol: string;
19-
image: string;
20-
}) {
21-
const { prices, connected } = useCoinPrice([coinId]);
22-
const priceData = prices[coinId];
23-
24-
const isTrendingUp = priceData
25-
? priceData.priceChangePercentage24h > 0
26-
: false;
14+
price,
15+
priceChangePercentage24h,
16+
volume24,
17+
rank,
18+
}: TopGainersLosers) {
19+
const isTrendingUp = priceChangePercentage24h > 0;
2720

2821
return (
29-
<Link href={`/coins/${coinId}`}>
22+
<Link href={`/coins/${id}`}>
3023
<div className='bg-dark-500 hover:bg-dark-400/50 transition-all rounded-lg p-5 border border-purple-600/20 hover:border-purple-600/50 cursor-pointer h-fit'>
3124
{/* Header */}
3225
<div className='flex gap-3 mb-4'>
@@ -44,57 +37,37 @@ export default function CoinCard({
4437
</div>
4538

4639
{/* Price */}
47-
{priceData ? (
48-
<>
49-
<div className='flex justify-between items-center mb-5'>
50-
<p className='text-2xl font-bold'>
51-
{formatPrice(priceData.price)}
52-
</p>
53-
{/* 24h Change */}
54-
<div className='flex items-center gap-2'>
55-
<Badge
56-
className={cn(
57-
'font-medium h-fit py-1 flex items-center gap-1',
58-
isTrendingUp
59-
? 'bg-green-500/20 text-green-600'
60-
: 'bg-red-500/20 text-red-500'
61-
)}
62-
>
63-
{formatPercentage(priceData.priceChangePercentage24h)}
64-
{isTrendingUp ? <TrendingUp /> : <TrendingDown />}
65-
</Badge>
66-
</div>
40+
<>
41+
<div className='flex justify-between items-center mb-5'>
42+
<p className='text-xl font-bold'>{formatPrice(price, 7)}</p>
43+
{/* 24h Change */}
44+
<div className='flex items-center gap-2'>
45+
<Badge
46+
className={cn(
47+
'font-medium h-fit py-1 flex items-center gap-1',
48+
isTrendingUp
49+
? 'bg-green-500/20 text-green-600'
50+
: 'bg-red-500/20 text-red-500'
51+
)}
52+
>
53+
{formatPercentage(priceChangePercentage24h)}
54+
{isTrendingUp ? <TrendingUp /> : <TrendingDown />}
55+
</Badge>
6756
</div>
57+
</div>
6858

69-
{/* Market Stats */}
70-
<div className='space-y-3 text-sm'>
71-
<div className='flex flex-row lg:flex-col xl:flex-row gap-1 justify-between'>
72-
<span className='text-gray-400'>Market Cap</span>
73-
<span className='font-medium'>
74-
{formatPrice(priceData.marketCap)}
75-
</span>
76-
</div>
77-
<div className='flex flex-row lg:flex-col xl:flex-row gap-1 justify-between'>
78-
<span className='text-gray-400'>Volume 24h</span>
79-
<span className='font-medium'>
80-
{formatPrice(priceData.volume24h)}
81-
</span>
82-
</div>
59+
{/* Market Stats */}
60+
<div className='space-y-3'>
61+
<div className='flex gap-1 justify-between'>
62+
<span className='text-purple-100'>Market Cap Rank</span>
63+
<span className='font-medium'>#{rank}</span>
8364
</div>
84-
</>
85-
) : (
86-
// Loading Skeleton
87-
<>
88-
<div className='flex justify-between items-center mb-5'>
89-
<div className='h-8 w-32 bg-dark-400/50 rounded animate-pulse' />
90-
<div className='h-7 w-24 bg-dark-400/50 rounded-full animate-pulse' />
65+
<div className='flex gap-1 justify-between'>
66+
<span className='text-purple-100'>Volume 24h: </span>
67+
<span className='font-medium'>{formatPrice(volume24)}</span>
9168
</div>
92-
<div className='space-y-3 text-sm'>
93-
<div className='h-5 w-full bg-dark-400/50 rounded animate-pulse' />
94-
<div className='h-5 w-full bg-dark-400/50 rounded animate-pulse' />
95-
</div>
96-
</>
97-
)}
69+
</div>
70+
</>
9871
</div>
9972
</Link>
10073
);

components/Converter.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const Converter = ({ symbol, icon, priceList }: ConverterProps) => {
4141
<div className='relative flex justify-center items-center my-4'>
4242
<div className='h-[1px] z-10 w-full bg-dark-400 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 ' />
4343
<Image
44-
src='/assets/icons/converter.svg'
44+
src='/assets/converter.svg'
4545
alt='converter'
4646
width={32}
4747
height={32}
@@ -52,7 +52,7 @@ export const Converter = ({ symbol, icon, priceList }: ConverterProps) => {
5252

5353
<div className='bg-dark-400 h-12 w-full rounded-md flex items-center justify-between py-4 pl-4'>
5454
<p className='text-base font-medium'>
55-
{formatPrice(convertedPrice, currency, false)}
55+
{formatPrice(convertedPrice, 2, currency, false)}
5656
</p>
5757
<Select value={currency} onValueChange={setCurrency}>
5858
<SelectTrigger

components/Header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const Header = ({
1919
<div className='container flex justify-between items-center h-full'>
2020
<Link href='/'>
2121
<Image
22-
src='/assets/images/logo.svg'
22+
src='/assets/logo.svg'
2323
alt='CoinPulse Logo'
2424
width={132}
2525
height={40}

lib/ coingecko.actions.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,6 @@ export async function getCoinOHLC(
5555
return res.json();
5656
}
5757

58-
export async function getTopPoolForToken(
59-
network: string,
60-
tokenAddress: string
61-
) {
62-
const res = await fetch(
63-
`${baseUrl}/onchain/networks/${network}/tokens/${tokenAddress}/pools`,
64-
header
65-
);
66-
67-
if (!res.ok) throw new Error('Failed to fetch pool data');
68-
const data = await res.json();
69-
70-
return data.data && data.data.length > 0 ? data.data[0] : null;
71-
}
72-
7358
export async function getTrendingCoins() {
7459
const res = await fetch(`${baseUrl}/search/trending`, header);
7560

@@ -79,6 +64,15 @@ export async function getTrendingCoins() {
7964
return data.coins || [];
8065
}
8166

67+
export async function getTopGainersLosers() {
68+
const res = await fetch(`${baseUrl}/coins/top_gainers_losers?vs_currency=usd`, header);
69+
70+
if (!res.ok) throw new Error('Failed to fetch top gainers/losers');
71+
72+
const data = await res.json();
73+
return { top_gainers: data.top_gainers.slice(0,4) || [], top_losers: data.top_losers.slice(0,4) || [] };
74+
}
75+
8276
export async function searchCoins(query: string): Promise<SearchCoin[]> {
8377
if (!query || query.trim().length === 0) return [];
8478

lib/utils.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ export function cn(...inputs: ClassValue[]) {
88

99
export function formatPrice(
1010
value: number | null | undefined,
11+
digits?: number,
1112
currency?: string,
12-
showSymbol?: boolean
13+
showSymbol?: boolean,
1314
) {
1415
if (value === null || value === undefined || isNaN(value)) {
1516
return showSymbol !== false ? '$0.00' : '0.00';
@@ -19,13 +20,13 @@ export function formatPrice(
1920
return value.toLocaleString(undefined, {
2021
style: 'currency',
2122
currency: currency?.toUpperCase() || 'USD',
22-
minimumFractionDigits: 2,
23-
maximumFractionDigits: 2,
23+
minimumFractionDigits: digits ?? 2,
24+
maximumFractionDigits: digits ?? 2,
2425
});
2526
}
2627
return value.toLocaleString(undefined, {
27-
minimumFractionDigits: 2,
28-
maximumFractionDigits: 2,
28+
minimumFractionDigits: digits ?? 2,
29+
maximumFractionDigits: digits ?? 2,
2930
});
3031
}
3132

types.d.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,30 @@ interface PricesMap {
148148
[coinId: string]: CoinPriceData;
149149
}
150150

151-
152151
type UseCoinGeckoSocketProps = {
153152
channel: string;
154153
onReady: (socket: WebSocket) => void;
155154
onData: (data: any) => void;
156-
};
155+
};
156+
157+
interface TopGainersLosers {
158+
id: string;
159+
name: string;
160+
symbol: string;
161+
image: string;
162+
price: number;
163+
priceChangePercentage24h: number;
164+
volume24: number;
165+
rank: number;
166+
}
167+
168+
interface TopGainersLosersResponse {
169+
id: string;
170+
name: string;
171+
symbol: string;
172+
image: string;
173+
usd: number;
174+
usd_24h_change: number;
175+
usd_24h_vol: number;
176+
market_cap_rank: number;
177+
}

0 commit comments

Comments
 (0)