Skip to content

Commit

Permalink
Merge pull request #8 from bonz88/feat/table-coins
Browse files Browse the repository at this point in the history
feat/table-coins
  • Loading branch information
abhishekjakhar committed Mar 23, 2024
2 parents c90c45e + 832c7b6 commit 8d6d665
Show file tree
Hide file tree
Showing 8 changed files with 493 additions and 3 deletions.
157 changes: 157 additions & 0 deletions src/app/components/CoinsMarketStats.tsx
@@ -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>
</>
);
}
68 changes: 68 additions & 0 deletions src/app/components/TableCoins.tsx
@@ -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>
);
}
32 changes: 32 additions & 0 deletions src/app/components/TableGraph.tsx
@@ -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>
);
}
117 changes: 117 additions & 0 deletions src/app/components/ui/table.tsx
@@ -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,
};
12 changes: 9 additions & 3 deletions src/app/icons/ChevronDownIcon.tsx
@@ -1,10 +1,16 @@
export const ChevronDownIcon = () => {
return (
<svg width="17" height="16" viewBox="0 0 17 16" fill="none">
<svg
width="8"
height="4"
viewBox="0 0 8 4"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.39974 9.66699L11.7331 6.33366L5.06641 6.33366L8.39974 9.66699Z"
d="M3.99935 3.66699L7.33268 0.333659L0.666016 0.333659L3.99935 3.66699Z"
fill="#FE2264"
style={{ fill: "#FE2264", fillOpacity: 1 }}
style={{ fillOpacity: 1 }}
/>
</svg>
);
Expand Down

0 comments on commit 8d6d665

Please sign in to comment.