Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(ui-ux): added dashboard stats #292

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion src/api/LatestDataApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ import {
MAIN_LATEST_TRANSACTION_URL,
wrapResponse,
} from "./index";
import { BlockProps, RawTransactionI } from "./types";
import {
BlockProps,
ChartDataProps,
DashboardStatsProps,
RawTransactionI,
SmartContractStatsProps,
} from "./types";

const MAX_ROW = 5;

Expand Down Expand Up @@ -76,4 +82,25 @@ export default {
};
});
},
getDashboardStats: async (
network: NetworkConnection
): Promise<DashboardStatsProps> => {
const baseUrl = getBaseUrl(network);
const res = await fetch(`${baseUrl}/api/v2/stats`);
return wrapResponse<DashboardStatsProps>(res);
},
getSmartContractStats: async (
network: NetworkConnection
): Promise<SmartContractStatsProps> => {
const baseUrl = getBaseUrl(network);
const res = await fetch(`${baseUrl}/api/v2/smart-contracts/counters`);
return wrapResponse<SmartContractStatsProps>(res);
},
getTxnChartsData: async (
network: NetworkConnection
): Promise<ChartDataProps> => {
const baseUrl = getBaseUrl(network);
const res = await fetch(`${baseUrl}/api/v2/stats/charts/transactions`);
return wrapResponse<ChartDataProps>(res);
},
};
37 changes: 37 additions & 0 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,43 @@ export interface RawTransactionV1 {
status: string;
}

export interface DashboardStatsProps {
average_block_time: number;
coin_price: string;
gas_prices: {
average: number;
fast: number;
slow: number;
};
average: number;
fast: number;
slow: number;
gas_used_today: string;
market_cap: string;
network_utilization_percentage: number;
static_gas_price: string | null;
total_addresses: string;
total_blocks: string;
total_gas_used: string;
total_transactions: string;
transactions_today: string;
}

export interface SmartContractStatsProps {
new_smart_contracts_24h: string;
new_verified_smart_contracts_24h: string;
smart_contracts: string;
verified_smart_contracts: string;
}
export interface ChartDataI {
date: string;
tx_count: number;
}

export interface ChartDataProps {
chart_data: ChartDataI[];
}

export interface AddressProps {
hash: string;
implementation_name: string | null;
Expand Down
140 changes: 113 additions & 27 deletions src/components/GroupStatisticCard.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,120 @@
import React from "react";
import { useState, useEffect } from "react";
import StatisticCard from "@components/commons/StatisticCard";
import clsx from "clsx";
import { MdCheckCircle } from "react-icons/md";
import LatestDataApi from "@api/LatestDataApi";
import { useNetwork } from "@contexts/NetworkContext";
import BigNumber from "bignumber.js";
import { DashboardStatsProps, SmartContractStatsProps } from "@api/types";
import { GWEI_SYMBOL } from "shared/constants";
import NumericFormat from "./commons/NumericFormat";

export default function GroupStatisticCard() {
const { connection } = useNetwork();
const [isLoading, setIsLoading] = useState(false);
const [dashboardStats, setDashboardStats] = useState<DashboardStatsProps>();
const [smartContractStats, setSmartContractStats] =
useState<SmartContractStatsProps>();
const fetchStats = async () => {
setIsLoading(true);
const dashStats = await LatestDataApi.getDashboardStats(connection);
setDashboardStats(dashStats);
const scStats = await LatestDataApi.getSmartContractStats(connection);
setSmartContractStats(scStats);
setIsLoading(false);
};

useEffect(() => {
fetchStats();
}, []);

function GroupStatisticCard() {
const groupStatsCardContent = [
{
title: "24h total value locked",
body: "3232400000",
footer: "+2.34%",
testId: "24h-total-value-locked",
},
{
title: "24h transactions",
body: "32324000000",
footer: "-0.02%",
testId: "24h-total-transactions",
},
];
return (
<div className="flex flex-col space-y-2 lg:space-y-0 md:space-y-0 lg:space-x-2 md:space-x-2 lg:flex-row md:flex-row">
{groupStatsCardContent.map((card) => (
<div className="mt-11 md:mt-8 lg:mt-[72px]">
<div
className={clsx(
"grid grid-cols-1 gap-4",
"grid md:grid-cols-2 gap-6",
"grid lg:grid-cols-4 gap-6"
)}
>
<StatisticCard
key={card.title}
title={card.title}
body={card.body}
footer={card.footer}
testId={card.testId}
/>
))}
title="Blocks"
value={new BigNumber(dashboardStats?.total_blocks ?? 0)}
testId="blocks"
isLoading={isLoading}
>
<span>
{`Avg. speed: ${new BigNumber(
dashboardStats?.average_block_time ?? 0
)
.dividedBy(1000)
.toFixed(1)} sec`}
</span>
</StatisticCard>

<StatisticCard
title="Transactions"
value={new BigNumber(dashboardStats?.total_transactions ?? 0)}
testId="transactions"
isLoading={isLoading}
>
<NumericFormat
value={new BigNumber(dashboardStats?.transactions_today ?? 0)}
thousandSeparator
prefix={`Today: `}
suffix={` transactions`}
decimalScale={0}
/>
</StatisticCard>

<StatisticCard
title="Contracts"
value={
new BigNumber(smartContractStats?.verified_smart_contracts ?? 0)
}
testId="contracts"
isLoading={isLoading}
>
<span className="flex flex-row items-center">
<NumericFormat
value={new BigNumber(
smartContractStats?.verified_smart_contracts ?? 0
)
.multipliedBy(smartContractStats?.smart_contracts ?? 0)
.dividedBy(100)
.toFixed(2)}
thousandSeparator
suffix="% verified"
decimalScale={2}
/>
<MdCheckCircle className="h-5 w-5 ml-1 text-green-800" />
</span>
</StatisticCard>

<StatisticCard
title="Gas price"
value={new BigNumber(dashboardStats?.gas_prices.average ?? 0)}
testId="gas-price"
suffix={` ${GWEI_SYMBOL}`}
isLoading={isLoading}
>
<NumericFormat
value={new BigNumber(dashboardStats?.gas_prices.fast ?? 0)}
thousandSeparator
prefix={`High: `}
suffix={` ${GWEI_SYMBOL}`}
decimalScale={0}
/>
<NumericFormat
value={new BigNumber(dashboardStats?.gas_prices.slow ?? 0)}
thousandSeparator
prefix={`Low: `}
suffix={` ${GWEI_SYMBOL}`}
decimalScale={0}
className="ml-2"
/>
</StatisticCard>
</div>
</div>
);
}

export default GroupStatisticCard;
106 changes: 25 additions & 81 deletions src/components/TokenStatsDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import clsx from "clsx";
import TokenStatsApi from "@api/TokenStatsApi";
import { useUnitSuffix } from "hooks/useUnitSuffix";
import { DMX_TOKEN_SYMBOL } from "shared/constants";
import TrendLineChart, { LineData } from "./TrendLineChart";
import GradientCardContainer from "./commons/GradientCardContainer";
import { useState, useEffect } from "react";
import LatestDataApi from "@api/LatestDataApi";
import { useNetwork } from "@contexts/NetworkContext";
import { ChartDataI } from "@api/types";
import TransactionsLineChart from "./TransactionsLineChart";

export interface TokenStats {
tokenPrice: number;
Expand All @@ -14,87 +13,32 @@ export interface TokenStats {
}

export default function TokenStatsDisplay(): JSX.Element {
const stats: TokenStats = TokenStatsApi.useTokenStats();
const priceHistory = TokenStatsApi.useTokenPriceHistory();
const { connection } = useNetwork();
const [chartData, setChartData] = useState<ChartDataI[]>([]);

const trendLineData: LineData[] = priceHistory.map((data) => ({
value: data.price,
}));
const fetchStats = async () => {
const data = await LatestDataApi.getTxnChartsData(connection);
setChartData(data.chart_data?.reverse());
};

return (
<div className="pb-2 pt-6 lg:pb-6">
<GradientCardContainer>
<section className="flex flex-col md:flex-row md:flex-wrap xl:flex-nowrap xl:items-center xl:justify-between p-5 pb-10 md:p-10 xl:py-7 rounded-[15px]">
<section className="flex flex-1 items-center md:items-start xl:flex-1 xl:order-first xl:items-center">
<TokenPriceSection data={stats} />
</section>
<section className="xl:order-last xl:ml-16">
<DetailsSection data={stats} />
</section>
<section className="mt-8 md:mt-12 xl:mt-0 h-[88px] md:basis-full xl:order-2 xl:basis-auto xl:w-[304px]">
<TrendLineChart data={trendLineData} />
</section>
</section>
</GradientCardContainer>
</div>
);
}
useEffect(() => {
fetchStats();
}, []);

function TokenPriceSection({ data }: { data: TokenStats }) {
return (
<>
<div className="h-10 w-10 mb-1 md:mb-0 md:mt-1 xl:mt-0 md:h-[39px] md:w-[39px] xl:h-[46px] xl:w-[46px] rounded-full bg-gradient-to-r from-green-800 to-blue-800" />
<div className="flex flex-1 md:flex-col xl:flex-row justify-between md:justify-start xl:items-end ml-2 md:ml-3">
<div>
<div className="text-white-700 text-xs md:text-sm">
DefiMetaChain Token
</div>
<div className="text-white-50 font-bold text-2xl xl:text-[32px] xl:leading-10 pt-1 md:pt-0">
{DMX_TOKEN_SYMBOL}
</div>
</div>
<div className="flex-wrap md:flex md:items-baseline md:pt-2 xl:pt-0 xl:pl-3">
<div className="text-white-50 text-right text-lg md:text-2xl xl:text-[28px] leading-5 md:leading-10">
${data.tokenPrice}
</div>
<div
className={clsx(
"text-right text-sm pt-2 md:pt-0 md:pl-2",
data.percentChange < 0 ? "text-red-800" : "text-green-800"
)}
>
{data.percentChange >= 0 ? "+" : ""}
{data.percentChange}%
</div>
<div className="mb-4 mt-8 md:mt-2 lg:mt-12">
<div>
<div className="space-x-2 mb-6">
<span className="text-2xl text-white-50 font-semibold">
{" "}
Transaction history
</span>
<span className="text-sm text-white-50">in 30 days</span>
</div>
<section className="h-[200px]">
<TransactionsLineChart data={chartData} />
</section>
</div>
</>
);
}

function DetailsSection({ data }: { data: TokenStats }) {
const circulation = useUnitSuffix(data.circulation);
const last24hVolume = useUnitSuffix(data.last24hVolume);
const marketCap = useUnitSuffix(data.marketCap);
return (
<div className="pt-10 md:pt-0">
<DetailRow
label="Circulation"
value={`${circulation} ${DMX_TOKEN_SYMBOL}`}
/>
<DetailRow label="24h Volume" value={last24hVolume} />
<DetailRow label="Market Cap" value={`$${marketCap}`} />
</div>
);
}

function DetailRow({ label, value }: { label: string; value: string }) {
const labelStyle = "text-white-700 md:pr-2";
const valueStyle = "text-right text-white-50";
return (
<div className="flex justify-between md:justify-end pt-2 md:pt-3 xl:pt-2 first:pt-0">
<div className={labelStyle}>{label}</div>
<div className={valueStyle}>{value}</div>
</div>
);
}
Loading
Loading