Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from bonz88/feat/table-coins
feat/table-coins
- Loading branch information
Showing
8 changed files
with
493 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import Image from "next/image"; | ||
import { useAppSelector } from "../../redux/store"; | ||
import { ChevronUpIcon } from "../icons/ChevronUpIcon"; | ||
import { ChevronDownIcon } from "../icons/ChevronDownIcon"; | ||
import { Progress } from "../components/ui/progress"; | ||
import formatNumber from "../../app/utils/formatNumber"; | ||
import TableGraph from "./TableGraph"; | ||
import { TableCell, TableRow } from "./ui/table"; | ||
|
||
type Coin = { | ||
id: string; | ||
image: string; | ||
symbol: string; | ||
current_price: number; | ||
price_change_percentage_1h_in_currency: number; | ||
price_change_percentage_24h_in_currency: number; | ||
price_change_percentage_7d_in_currency: number; | ||
total_volume: number; | ||
market_cap: number; | ||
circulating_supply: number; | ||
total_supply: number; | ||
sparkline_in_7d: SparklineIn7d; | ||
}; | ||
|
||
type SparklineIn7d = { | ||
price: number[]; | ||
}; | ||
|
||
type CoinsMarketStatsProps = { | ||
coin: Coin; | ||
index: number; | ||
}; | ||
|
||
export default function CoinsMarketStats({ | ||
coin, | ||
index, | ||
}: CoinsMarketStatsProps) { | ||
const currencySymbol = useAppSelector( | ||
(state) => state.currency.currentCurrency.symbol | ||
); | ||
return ( | ||
<> | ||
<tr className="bg-transparent h-2"></tr> | ||
<TableRow className="dark:bg-[#191925] bg-white border-0"> | ||
<TableCell className="dark:text-white text-[#232336] text-base font-medium leading-6 rounded-tl-xl rounded-bl-xl"> | ||
{index + 1} | ||
</TableCell> | ||
<TableCell> | ||
<div className="flex items-center gap-4"> | ||
<Image src={coin.image} alt={coin.id} width={32} height={32} /> | ||
<> | ||
<span className="dark:text-white text-[#232336] text-base font-medium capitalize leading-6"> | ||
{coin.id} | ||
</span> | ||
<span className="dark:text-white text-[#232336] text-base font-medium leading-6 uppercase"> | ||
({coin.symbol}) | ||
</span> | ||
</> | ||
</div> | ||
</TableCell> | ||
<TableCell className="dark:text-white text-[#232336] text-base font-medium leading-6"> | ||
{currencySymbol} | ||
{coin.current_price} | ||
</TableCell> | ||
<TableCell> | ||
<div className="flex items-center gap-[6px]"> | ||
{coin.price_change_percentage_1h_in_currency > 0 ? ( | ||
<ChevronUpIcon /> | ||
) : ( | ||
<ChevronDownIcon /> | ||
)} | ||
<span | ||
className={`text-base font-medium leading-6 ${ | ||
coin.price_change_percentage_1h_in_currency > 0 | ||
? "text-[#00F0E2]" | ||
: "text-[#FD2263]" | ||
}`} | ||
>{`${Math.abs(coin.price_change_percentage_1h_in_currency).toFixed( | ||
2 | ||
)}%`}</span> | ||
</div> | ||
</TableCell> | ||
<TableCell> | ||
<div className="flex items-center gap-[6px]"> | ||
{coin.price_change_percentage_24h_in_currency > 0 ? ( | ||
<ChevronUpIcon /> | ||
) : ( | ||
<ChevronDownIcon /> | ||
)} | ||
<span | ||
className={`text-base font-medium leading-6 ${ | ||
coin.price_change_percentage_24h_in_currency > 0 | ||
? "text-[#00F0E2]" | ||
: "text-[#FD2263]" | ||
}`} | ||
>{`${Math.abs(coin.price_change_percentage_24h_in_currency).toFixed( | ||
2 | ||
)}%`}</span> | ||
</div> | ||
</TableCell> | ||
<TableCell> | ||
<div className="flex items-center gap-[6px]"> | ||
{coin.price_change_percentage_7d_in_currency > 0 ? ( | ||
<ChevronUpIcon /> | ||
) : ( | ||
<ChevronDownIcon /> | ||
)} | ||
<span | ||
className={`text-base font-medium leading-6 ${ | ||
coin.price_change_percentage_7d_in_currency > 0 | ||
? "text-[#00F0E2]" | ||
: "text-[#FD2263]" | ||
}`} | ||
>{`${Math.abs(coin.price_change_percentage_7d_in_currency).toFixed( | ||
2 | ||
)}%`}</span> | ||
</div> | ||
</TableCell> | ||
<TableCell className="dark:text-white text-[#232336] text-base font-medium leading-6"> | ||
<div className="flex justify-between"> | ||
<span className="text-xs font-normal"> | ||
{currencySymbol} | ||
{formatNumber(coin.total_volume)} | ||
</span> | ||
<span className="text-xs font-normal"> | ||
{currencySymbol} | ||
{formatNumber(coin.market_cap)} | ||
</span> | ||
</div> | ||
<Progress | ||
className="w-full h-[6px] bg-gray-500" | ||
value={(coin.total_volume / coin.market_cap) * 100} | ||
indicator="bg-[#F7931A]" | ||
/> | ||
</TableCell> | ||
<TableCell className="dark:text-white text-[#232336] text-base font-medium leading-6"> | ||
<div className="flex justify-between"> | ||
<span className="text-xs font-normal"> | ||
{formatNumber(coin.circulating_supply)} | ||
</span> | ||
<span className="text-xs font-normal"> | ||
{formatNumber(coin.total_supply)} | ||
</span> | ||
</div> | ||
<Progress | ||
className="w-full h-[6px] bg-gray-500" | ||
value={(coin.circulating_supply / coin.total_supply) * 100} | ||
indicator="bg-[#F7931A]" | ||
/> | ||
</TableCell> | ||
<TableCell className="dark:text-white text-[#232336] text-base font-medium leading-6 rounded-tr-xl rounded-br-xl"> | ||
<TableGraph sparklineIn7Days={coin.sparkline_in_7d.price} /> | ||
</TableCell> | ||
</TableRow> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
"use client"; | ||
import { useEffect } from "react"; | ||
import { useDispatch } from "react-redux"; | ||
import { AppDispatch, useAppSelector } from "../../redux/store"; | ||
import { getCoinMarketData } from "../../redux/features/coinsTableSlice"; | ||
import { Table, TableBody, TableHead, TableHeader, TableRow } from "./ui/table"; | ||
|
||
import CoinsMarketStats from "./CoinsMarketStats"; | ||
|
||
export default function TableCoins() { | ||
const dispatch: AppDispatch = useDispatch(); | ||
const { coins, hasError, currentPage } = useAppSelector( | ||
(state) => state.coinsTable | ||
); | ||
|
||
const currencyCode = useAppSelector( | ||
(state) => state.currency.currentCurrency.code | ||
); | ||
|
||
useEffect(() => { | ||
dispatch(getCoinMarketData({ currency: currencyCode, page: currentPage })); | ||
}, [dispatch, currencyCode, currentPage]); | ||
|
||
if (hasError) { | ||
return <div>Error fetching data</div>; | ||
} | ||
|
||
return ( | ||
<Table> | ||
<TableHeader> | ||
<TableRow> | ||
<TableHead className="text-sm font-normal dark:text-[#D1D1D1] text-[#424286] leading-4"> | ||
# | ||
</TableHead> | ||
<TableHead className="text-sm font-normal dark:text-[#D1D1D1] text-[#424286] leading-4"> | ||
Name | ||
</TableHead> | ||
<TableHead className="text-sm font-normal dark:text-[#D1D1D1] text-[#424286] leading-4"> | ||
Price | ||
</TableHead> | ||
<TableHead className="text-sm font-normal dark:text-[#D1D1D1] text-[#424286] leading-4"> | ||
1h% | ||
</TableHead> | ||
<TableHead className="text-sm font-normal dark:text-[#D1D1D1] text-[#424286] leading-4"> | ||
24h% | ||
</TableHead> | ||
<TableHead className="text-sm font-normal dark:text-[#D1D1D1] text-[#424286] leading-4"> | ||
7d% | ||
</TableHead> | ||
<TableHead className="text-sm font-normal dark:text-[#D1D1D1] text-[#424286] leading-4"> | ||
24h volume / Market Cap | ||
</TableHead> | ||
<TableHead className="text-sm font-normal dark:text-[#D1D1D1] text-[#424286] leading-4"> | ||
Circulating / Total supply | ||
</TableHead> | ||
<TableHead className="w-[162px] text-sm font-normal dark:text-[#D1D1D1] text-[#424286] leading-4"> | ||
Last 7d | ||
</TableHead> | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{coins.map((coin, index) => ( | ||
<CoinsMarketStats coin={coin} index={index} key={coin.id} /> | ||
))} | ||
</TableBody> | ||
</Table> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
"use client"; | ||
import { ResponsiveContainer, AreaChart, Area, YAxis } from "recharts"; | ||
|
||
type TableGraphProps = { | ||
sparklineIn7Days: number[]; | ||
}; | ||
|
||
export default function TableGraph({ sparklineIn7Days }: TableGraphProps) { | ||
const formattedData = sparklineIn7Days.map((price) => ({ | ||
price, | ||
})); | ||
return ( | ||
<ResponsiveContainer width="100%" height={50}> | ||
<AreaChart data={formattedData}> | ||
<defs> | ||
<linearGradient id="colorAreaChart" x1="0" y1="0" x2="0" y2="1"> | ||
<stop offset="80%" stopColor="#7474F2" stopOpacity={0.4} /> | ||
<stop offset="95%" stopColor="#7474F2" stopOpacity={0.1} /> | ||
</linearGradient> | ||
</defs> | ||
<YAxis hide={true} domain={["dataMin", "dataMax"]} /> | ||
<Area | ||
type="monotone" | ||
dataKey="price" | ||
stroke="#7878FA" | ||
strokeWidth="2" | ||
fill="url(#colorAreaChart)" | ||
/> | ||
</AreaChart> | ||
</ResponsiveContainer> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import * as React from "react"; | ||
|
||
import { cn } from "@/lib/utils"; | ||
|
||
const Table = React.forwardRef< | ||
HTMLTableElement, | ||
React.HTMLAttributes<HTMLTableElement> | ||
>(({ className, ...props }, ref) => ( | ||
<div className="relative w-full overflow-auto"> | ||
<table | ||
ref={ref} | ||
className={cn("w-full caption-bottom text-sm", className)} | ||
{...props} | ||
/> | ||
</div> | ||
)); | ||
Table.displayName = "Table"; | ||
|
||
const TableHeader = React.forwardRef< | ||
HTMLTableSectionElement, | ||
React.HTMLAttributes<HTMLTableSectionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<thead ref={ref} className={cn("[&_tr]:border-0", className)} {...props} /> | ||
)); | ||
TableHeader.displayName = "TableHeader"; | ||
|
||
const TableBody = React.forwardRef< | ||
HTMLTableSectionElement, | ||
React.HTMLAttributes<HTMLTableSectionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<tbody | ||
ref={ref} | ||
className={cn("[&_tr:last-child]:border-0", className)} | ||
{...props} | ||
/> | ||
)); | ||
TableBody.displayName = "TableBody"; | ||
|
||
const TableFooter = React.forwardRef< | ||
HTMLTableSectionElement, | ||
React.HTMLAttributes<HTMLTableSectionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<tfoot | ||
ref={ref} | ||
className={cn( | ||
"border-t bg-slate-100/50 font-medium [&>tr]:last:border-b-0 dark:bg-slate-800/50", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)); | ||
TableFooter.displayName = "TableFooter"; | ||
|
||
const TableRow = React.forwardRef< | ||
HTMLTableRowElement, | ||
React.HTMLAttributes<HTMLTableRowElement> | ||
>(({ className, ...props }, ref) => ( | ||
<tr | ||
ref={ref} | ||
className={cn( | ||
"border-b transition-colors data-[state=selected]:bg-slate-100 dark:data-[state=selected]:bg-slate-800", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)); | ||
TableRow.displayName = "TableRow"; | ||
|
||
const TableHead = React.forwardRef< | ||
HTMLTableCellElement, | ||
React.ThHTMLAttributes<HTMLTableCellElement> | ||
>(({ className, ...props }, ref) => ( | ||
<th | ||
ref={ref} | ||
className={cn( | ||
"h-12 px-4 text-left align-middle font-medium text-slate-500 [&:has([role=checkbox])]:pr-0 dark:text-slate-400", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)); | ||
TableHead.displayName = "TableHead"; | ||
|
||
const TableCell = React.forwardRef< | ||
HTMLTableCellElement, | ||
React.TdHTMLAttributes<HTMLTableCellElement> | ||
>(({ className, ...props }, ref) => ( | ||
<td | ||
ref={ref} | ||
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)} | ||
{...props} | ||
/> | ||
)); | ||
TableCell.displayName = "TableCell"; | ||
|
||
const TableCaption = React.forwardRef< | ||
HTMLTableCaptionElement, | ||
React.HTMLAttributes<HTMLTableCaptionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<caption | ||
ref={ref} | ||
className={cn("mt-4 text-sm text-slate-500 dark:text-slate-400", className)} | ||
{...props} | ||
/> | ||
)); | ||
TableCaption.displayName = "TableCaption"; | ||
|
||
export { | ||
Table, | ||
TableHeader, | ||
TableBody, | ||
TableFooter, | ||
TableHead, | ||
TableRow, | ||
TableCell, | ||
TableCaption, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.