Add ExplorerCard

This commit is contained in:
Yana
2024-11-24 21:25:23 +07:00
parent 399e4b1abd
commit fbd8cc5b4d
6 changed files with 2074 additions and 1615 deletions
+44 -12
View File
@@ -1,5 +1,10 @@
import { Card, CardHeader, CardContent, Typography, Box } from "@mui/material";
import React, { FC, ReactElement, ReactEventHandler } from "react";
import { ExplorerLineChart, IExplorerLineChartData } from "./ExplorerLineChart";
import {
ExplorerProgressBar,
IExplorerProgressBarProps,
} from "./ExplorerProgressBar";
interface ICardUpDownPriceLineProps {
percentage: number;
@@ -10,8 +15,8 @@ const CardUpDownPriceLine = (
): ReactElement => {
const { percentage, priceWentUp } = props;
return (
<Box>
<Typography>{percentage} (24H)</Typography>
<Box mb={3}>
<Typography sx={{ color: "#00CA33" }}>{percentage}% (24H)</Typography>
</Box>
);
};
@@ -23,10 +28,10 @@ interface ICardTitlePriceProps {
const CardTitlePrice = (props: ICardTitlePriceProps): React.ReactNode => {
const { price, upDownLine } = props;
return (
<Box>
<Box>
<Box display={"flex"} flexDirection={"column"} alignItems={"flex-end"}>
<Box display={"flex"} justifyContent={"space-between"} width={"100%"}>
<Typography>NYM</Typography>
<Typography>{price}</Typography>
<Typography>${price}</Typography>
</Box>
<CardUpDownPriceLine {...upDownLine} />
</Box>
@@ -39,10 +44,17 @@ interface ICardDataRowsProps {
const CardDataRows = (props: ICardDataRowsProps): React.ReactNode => {
const { rows } = props;
return (
<Box>
<Box mb={3}>
{rows.map((row, i) => {
return (
<Box key={i}>
<Box
key={i}
paddingTop={2}
paddingBottom={2}
display={"flex"}
justifyContent={"space-between"}
borderBottom={i === 0 ? "1px solid #fff" : "none"}
>
<Typography>{row.key}</Typography>
<Typography>{row.value}</Typography>
</Box>
@@ -58,8 +70,8 @@ type ContentCardProps = {
upDownLine?: ICardUpDownPriceLineProps;
titlePrice?: ICardTitlePriceProps;
dataRows?: ICardDataRowsProps;
graph?: React.ReactNode;
progressLineGraph?: React.ReactNode;
graph?: Array<IExplorerLineChartData>;
progressBar?: IExplorerProgressBarProps;
paragraph?: string;
onClick?: ReactEventHandler;
};
@@ -71,16 +83,36 @@ export const ExplorerCard: FC<ContentCardProps> = ({
upDownLine,
dataRows,
graph,
progressLineGraph,
progressBar,
paragraph,
onClick,
}) => (
<Card onClick={onClick} sx={{ height: "100%" }}>
<CardContent>
{overTitle && <Typography>{overTitle}</Typography>}
{title && <Typography>{title}</Typography>}
{overTitle && (
<Typography fontSize={14} mb={3}>
{overTitle}
</Typography>
)}
{title && (
<Typography fontSize={24} mb={!upDownLine ? 3 : 0}>
{title}
</Typography>
)}
{upDownLine && <CardUpDownPriceLine {...upDownLine} />}
{titlePrice && <CardTitlePrice {...titlePrice} />}
{dataRows && <CardDataRows {...dataRows} />}
{graph && (
<Box mb={3}>
<ExplorerLineChart data={graph} />
</Box>
)}
{progressBar && (
<Box mb={3}>
<ExplorerProgressBar {...progressBar} />
</Box>
)}
{paragraph && <Typography>{paragraph}</Typography>}
</CardContent>
</Card>
);
@@ -11,7 +11,7 @@ const LineChart = dynamic(
}
);
interface IExplorerLineChartData {
export interface IExplorerLineChartData {
date_utc: string;
greenLineNumericData: number;
purpleLineNumericData: number;
@@ -29,7 +29,7 @@ interface ILineAxes {
data: Array<IAxes>;
}
export const PacketsLineChart = ({
export const ExplorerLineChart = ({
data,
}: {
data: Array<IExplorerLineChartData>;
@@ -60,14 +60,14 @@ export const PacketsLineChart = ({
data.map((item: any) => {
const axesGreenLineData: IAxes = {
x: new Date(item.date_utc),
y: item.numericData1,
y: item.greenLineNumericData,
};
greenLineData.data.push(axesGreenLineData);
const axesPurpleLineData: IAxes = {
x: new Date(item.date_utc),
y: item.numericData2,
y: item.purpleLineNumericData,
};
purpleLineData.data.push(axesPurpleLineData);
@@ -102,8 +102,8 @@ export const PacketsLineChart = ({
enableSlices="x"
margin={{
bottom: 24,
left: 36,
right: 20,
left: 16,
right: 16,
top: 20,
}}
theme={{
@@ -129,7 +129,7 @@ export const PacketsLineChart = ({
type: "time",
format: "%Y-%m-%d",
}}
yScale={{ min: 150000000, type: "linear" }}
yScale={{ min: 1, type: "linear" }}
xFormat="time:%Y-%m-%d"
axisLeft={{
legendOffset: 12,
@@ -140,7 +140,7 @@ export const PacketsLineChart = ({
axisBottom={{
format: "%b %d",
legendOffset: -12,
tickValues: "every 2 days",
tickValues: "every day",
}}
/>
)}
@@ -0,0 +1,67 @@
import * as React from "react";
import Box from "@mui/material/Box";
import LinearProgress from "@mui/material/LinearProgress";
import { Typography } from "@mui/material";
export interface IExplorerProgressBarProps {
title?: string;
start: string; // Start timestamp as ISO 8601 string
end: string; // End timestamp as ISO 8601 string
showEpoch: boolean;
}
export const ExplorerProgressBar = (props: IExplorerProgressBarProps) => {
const { start, end, showEpoch, title } = props;
const [progress, setProgress] = React.useState(0);
React.useEffect(() => {
// Parse the timestamps
const startTime = new Date(start).getTime();
const endTime = new Date(end).getTime();
// Validate timestamps
if (isNaN(startTime) || isNaN(endTime)) {
console.error("Invalid start or end timestamp:", { start, end });
return;
}
// Function to calculate progress
const calculateProgress = () => {
const currentTime = Date.now();
if (currentTime < startTime) {
return 0;
}
if (currentTime >= endTime) {
return 100;
}
const elapsed = currentTime - startTime;
const total = endTime - startTime;
return (elapsed / total) * 100;
};
// Set initial progress and start timer
setProgress(calculateProgress());
const timer = setInterval(() => {
setProgress(calculateProgress());
}, 60000); // Update every minute (60000 milliseconds)
// Cleanup on unmount
return () => {
clearInterval(timer);
};
}, [start, end]);
return (
<Box sx={{ width: "100%" }}>
{title && <Typography mb={2}>{title}</Typography>}
<LinearProgress variant="determinate" value={progress} />
{showEpoch && (
<Box mt={2}>
<Typography>Start: {new Date(start).toLocaleString()}</Typography>
<Typography>End: {new Date(end).toLocaleString()}</Typography>
</Box>
)}
</Box>
);
};
+99 -32
View File
@@ -1,26 +1,90 @@
'use client'
"use client";
import React, { useEffect } from 'react'
import { Box, Grid, Link, Typography } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import OpenInNewIcon from '@mui/icons-material/OpenInNew'
import { PeopleAlt } from '@mui/icons-material'
import { Title } from '@/app/components/Title'
import { StatsCard } from '@/app/components/StatsCard'
import { MixnodesSVG } from '@/app/icons/MixnodesSVG'
import { Icons } from '@/app/components/Icons'
import { GatewaysSVG } from '@/app/icons/GatewaysSVG'
import { ValidatorsSVG } from '@/app/icons/ValidatorsSVG'
import { ContentCard } from '@/app/components/ContentCard'
import { WorldMap } from '@/app/components/WorldMap'
import { BIG_DIPPER } from '@/app/api/constants'
import { formatNumber } from '@/app/utils'
import { useMainContext } from './context/main'
import { useRouter } from 'next/navigation'
import React, { useEffect } from "react";
import { Box, Grid, Link, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
import { PeopleAlt } from "@mui/icons-material";
import { Title } from "@/app/components/Title";
import { StatsCard } from "@/app/components/StatsCard";
import { MixnodesSVG } from "@/app/icons/MixnodesSVG";
import { Icons } from "@/app/components/Icons";
import { GatewaysSVG } from "@/app/icons/GatewaysSVG";
import { ValidatorsSVG } from "@/app/icons/ValidatorsSVG";
import { ContentCard } from "@/app/components/ContentCard";
import { WorldMap } from "@/app/components/WorldMap";
import { BIG_DIPPER } from "@/app/api/constants";
import { formatNumber } from "@/app/utils";
import { useMainContext } from "./context/main";
import { useRouter } from "next/navigation";
import { ExplorerCard } from "./components/ExplorerCard";
// type ContentCardProps = {
// overTitle?: string;
// title?: string;
// upDownLine?: ICardUpDownPriceLineProps;
// titlePrice?: ICardTitlePriceProps;
// dataRows?: ICardDataRowsProps;
// graph?: Array<IExplorerLineChartData>;
// progressBar?: IExplorerProgressBarProps;
// paragraph?: string;
// onClick?: ReactEventHandler;
// };
const explorerCard = {
overTitle: "SINGLE",
title: "SINGLE",
upDownLine: {
percentage: 10,
priceWentUp: true,
},
titlePrice: {
price: 1.15,
upDownLine: {
percentage: 10,
priceWentUp: true,
},
},
dataRows: {
rows: [
{ key: "Market cap", value: "$ 1000000" },
{ key: "24H VOL", value: "$ 1000000" },
],
},
graph: [
{
date_utc: "2024-11-20",
greenLineNumericData: 10,
purpleLineNumericData: 5,
},
{
date_utc: "2024-11-21",
greenLineNumericData: 12,
purpleLineNumericData: 6,
},
{
date_utc: "2024-11-22",
greenLineNumericData: 9,
purpleLineNumericData: 5,
},
{
date_utc: "2024-11-23",
greenLineNumericData: 11,
purpleLineNumericData: 4,
},
],
progressBar: {
title: "Current NGM epoch",
start: "2024-11-24T13:26:19Z",
end: "2024-11-24T14:26:19Z",
showEpoch: true,
},
paragraph: "Additional line",
};
const PageOverview = () => {
const theme = useTheme()
const router = useRouter()
const theme = useTheme();
const router = useRouter();
const {
summaryOverview,
@@ -29,7 +93,7 @@ const PageOverview = () => {
block,
countryData,
serviceProviders,
} = useMainContext()
} = useMainContext();
return (
<Box component="main" sx={{ flexGrow: 1 }}>
<Grid>
@@ -40,19 +104,22 @@ const PageOverview = () => {
<Grid container spacing={3}>
{summaryOverview && (
<>
<Grid item xs={12} md={4}>
<ExplorerCard {...explorerCard} />
</Grid>
<Grid item xs={12} md={4}>
<StatsCard
onClick={() => router.push('/network-components/mixnodes')}
onClick={() => router.push("/network-components/mixnodes")}
title="Mixnodes"
icon={<MixnodesSVG />}
count={summaryOverview.data?.mixnodes.count || ''}
count={summaryOverview.data?.mixnodes.count || ""}
errorMsg={summaryOverview?.error}
/>
</Grid>
<Grid item xs={12} md={4}>
<StatsCard
onClick={() =>
router.push('/network-components/mixnodes?status=active')
router.push("/network-components/mixnodes?status=active")
}
title="Active nodes"
icon={<Icons.Mixnodes.Status.Active />}
@@ -66,7 +133,7 @@ const PageOverview = () => {
<Grid item xs={12} md={4}>
<StatsCard
onClick={() =>
router.push('/network-components/mixnodes?status=standby')
router.push("/network-components/mixnodes?status=standby")
}
title="Standby nodes"
color={
@@ -82,9 +149,9 @@ const PageOverview = () => {
{gateways && (
<Grid item xs={12} md={4}>
<StatsCard
onClick={() => router.push('/network-components/gateways')}
onClick={() => router.push("/network-components/gateways")}
title="Gateways"
count={gateways?.data?.length || ''}
count={gateways?.data?.length || ""}
errorMsg={gateways?.error}
icon={<GatewaysSVG />}
/>
@@ -94,7 +161,7 @@ const PageOverview = () => {
<Grid item xs={12} md={4}>
<StatsCard
onClick={() =>
router.push('/network-components/service-providers')
router.push("/network-components/service-providers")
}
title="Service providers"
icon={<PeopleAlt />}
@@ -108,7 +175,7 @@ const PageOverview = () => {
<StatsCard
onClick={() => window.open(`${BIG_DIPPER}/validators`)}
title="Validators"
count={validators?.data?.count || ''}
count={validators?.data?.count || ""}
errorMsg={validators?.error}
icon={<ValidatorsSVG />}
/>
@@ -150,7 +217,7 @@ const PageOverview = () => {
</Grid>
</Grid>
</Box>
)
}
);
};
export default PageOverview
export default PageOverview;
+6 -5
View File
@@ -9,15 +9,16 @@
"lint": "next lint"
},
"dependencies": {
"@mui/x-data-grid": "7.1.1",
"@mui/x-date-pickers": "7.1.1",
"@nivo/line": "^0.88.0",
"@nymproject/nym-validator-client": "0.18.0",
"@nymproject/react": "^1.0.0",
"@nymproject/nym-validator-client": "1.2.0",
"material-react-table": "^2.12.1",
"next": "14.1.4",
"react": "^18",
"react-dom": "^18",
"react-error-boundary": "^4.0.13",
"material-react-table": "^2.12.1",
"@mui/x-date-pickers": "7.1.1",
"@mui/x-data-grid": "7.1.1"
"react-error-boundary": "^4.0.13"
},
"devDependencies": {
"@types/node": "^20",
+1850 -1558
View File
File diff suppressed because it is too large Load Diff