mirror of
https://github.com/getwax/bls-frontend.git
synced 2026-01-08 03:43:52 -05:00
clean up dashboard
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-jss": "^10.10.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.20",
|
||||
"recharts": "^2.6.2",
|
||||
"typescript": "^4.9.5",
|
||||
"web-vitals": "^2.1.4"
|
||||
|
||||
163
src/App.tsx
163
src/App.tsx
@@ -1,98 +1,155 @@
|
||||
import Counter from 'components/Counter';
|
||||
import { CHARTS, COUNTERS } from 'data';
|
||||
import { CHARTS, COUNTERS, SECTIONS } from 'data';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { createUseStyles } from 'react-jss';
|
||||
import Flex from 'components/Flex';
|
||||
import ChartWrapper from 'components/Charts/ChartWrapper';
|
||||
import Typography from 'components/Typography';
|
||||
import logo from 'assets/images/waxGreenLogo.webp';
|
||||
|
||||
const { REACT_APP_API_URL } = process.env;
|
||||
|
||||
const useStyles = createUseStyles({
|
||||
card: {
|
||||
backgroundColor: '#34383D',
|
||||
border: '1px solid #74FFCE',
|
||||
borderRadius: '4px',
|
||||
color: '#74FFCE',
|
||||
padding: '20px',
|
||||
},
|
||||
container: {
|
||||
padding: '16px',
|
||||
padding: '16px 16px 120px 16px',
|
||||
},
|
||||
});
|
||||
|
||||
type Layout = {
|
||||
charts: any[];
|
||||
counters: any[];
|
||||
description?: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
function App() {
|
||||
const styles = useStyles();
|
||||
const [layout, setLayout] = useState<Layout>({
|
||||
charts: [],
|
||||
counters: [],
|
||||
});
|
||||
const [layout, setLayout] = useState<Layout[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const res = await fetch(`${REACT_APP_API_URL}/queries`);
|
||||
const data = await res.json();
|
||||
const charts = CHARTS.map((chart) => ({
|
||||
...chart,
|
||||
data: data[chart.queryId],
|
||||
}));
|
||||
const counters = COUNTERS.map((counter) => ({
|
||||
...counter,
|
||||
data: data[counter.queryId],
|
||||
}));
|
||||
setLayout({ counters, charts });
|
||||
|
||||
setLayout(
|
||||
SECTIONS.map((section) => {
|
||||
let charts: any[] = [];
|
||||
let counters: any[] = [];
|
||||
if (section.charts[0] !== undefined) {
|
||||
const range = section.charts;
|
||||
charts = range[1]
|
||||
? CHARTS.slice(range[0], range[1])
|
||||
: CHARTS.slice(range[0]);
|
||||
}
|
||||
if (section.counters[0] !== undefined) {
|
||||
const range = section.counters;
|
||||
counters = range[1]
|
||||
? COUNTERS.slice(range[0], range[1])
|
||||
: COUNTERS.slice(range[0]);
|
||||
}
|
||||
return {
|
||||
...section,
|
||||
charts: charts.map((chart) => ({
|
||||
...chart,
|
||||
data: data[chart.queryId],
|
||||
})),
|
||||
counters: counters.map((counter) => ({
|
||||
...counter,
|
||||
data: data[counter.queryId],
|
||||
})),
|
||||
};
|
||||
})
|
||||
);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Flex justifyContent='flex-end'>
|
||||
<a href='https://getwax.org' rel='noreferrer' target='_blank'>
|
||||
<img alt='Logo' src={logo} style={{ height: '40px' }} />
|
||||
</a>
|
||||
</Flex>
|
||||
<Flex
|
||||
alignItems='center'
|
||||
justifyContent='center'
|
||||
mb='32px'
|
||||
mt='32px'
|
||||
style={{ color: '#FCFCFC' }}
|
||||
mt='12px'
|
||||
style={{ color: '#74FFCE' }}
|
||||
>
|
||||
<Typography variant='h4'>BLS Wallet Analytics</Typography>
|
||||
<Typography variant='h2'>WAX V1 Analytics</Typography>
|
||||
</Flex>
|
||||
<Flex childFlex='1 0 calc(25% - 60px)' gap='6px' wrap='wrap'>
|
||||
{layout.counters.map((counter) => (
|
||||
<div className={styles.card} style={{ height: '200px' }}>
|
||||
<Typography style={{ color: '#FCFCFC' }} variant='h6'>
|
||||
{counter.title}
|
||||
</Typography>
|
||||
<Counter
|
||||
counterKey={counter.counterKey}
|
||||
data={counter.data}
|
||||
label={counter.label}
|
||||
/>
|
||||
{layout.map((section) => (
|
||||
<div>
|
||||
<div className={styles.card} style={{ marginBlock: '32px' }}>
|
||||
<Typography variant='h4'>{section.title}</Typography>
|
||||
{section.description && (
|
||||
<Typography style={{ marginTop: '4px' }} variant='subtitle1'>
|
||||
<i>{section.description}</i>
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
<Flex childFlex='1 0 calc(50% - 60px)' gap='6px' mt='16px' wrap='wrap'>
|
||||
{layout.charts.map((chart) => (
|
||||
<div className={styles.card} style={{ height: '350px' }}>
|
||||
<Typography
|
||||
style={{ color: '#FCFCFC', marginBottom: '8px' }}
|
||||
variant='h6'
|
||||
>
|
||||
{chart.title}
|
||||
</Typography>
|
||||
<ChartWrapper
|
||||
color={chart.color}
|
||||
chartType={chart.chartType}
|
||||
data={chart.data}
|
||||
stackBy={chart.stackBy}
|
||||
xAxisTitle={chart.xAxisTitle}
|
||||
xKey={chart.xKey}
|
||||
yAxisTitle={chart.yAxisTitle}
|
||||
yKey={chart.yKey}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
<Flex
|
||||
childFlex='1 0 calc(25% - 60px)'
|
||||
gap='6px'
|
||||
justifyContent='center'
|
||||
wrap='wrap'
|
||||
>
|
||||
{section.counters.map((counter) => (
|
||||
<div className={styles.card} style={{ height: '200px' }}>
|
||||
<Typography style={{ color: '#FCFCFC' }} variant='h6'>
|
||||
{''}
|
||||
</Typography>
|
||||
<Counter
|
||||
counterKey={counter.counterKey}
|
||||
data={counter.data}
|
||||
label={counter.label}
|
||||
showDecimal={counter.showDecimal}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
<Flex
|
||||
childFlex='1 0 calc(50% - 60px)'
|
||||
justifyContent='center'
|
||||
gap='6px'
|
||||
mt='16px'
|
||||
wrap='wrap'
|
||||
>
|
||||
{section.charts.map((chart) => (
|
||||
<div
|
||||
className={styles.card}
|
||||
style={{ height: '450px', maxWidth: '50%' }}
|
||||
>
|
||||
<Typography
|
||||
style={{ color: '#FCFCFC', marginBottom: '8px' }}
|
||||
variant='h6'
|
||||
>
|
||||
{chart.title}
|
||||
</Typography>
|
||||
<ChartWrapper
|
||||
color={chart.color}
|
||||
chartType={chart.chartType}
|
||||
data={chart.data}
|
||||
showDecimals={chart.showDecimals}
|
||||
stackBy={chart.stackBy}
|
||||
xAxisTitle={chart.xAxisTitle}
|
||||
xKey={chart.xKey}
|
||||
yAxisTitle={chart.yAxisTitle}
|
||||
yKey={chart.yKey}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
BIN
src/assets/images/waxGreenLogo.webp
Normal file
BIN
src/assets/images/waxGreenLogo.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
@@ -4,7 +4,6 @@ import {
|
||||
Area,
|
||||
AreaChart as RechartsAreaChart,
|
||||
CartesianGrid,
|
||||
ResponsiveContainer,
|
||||
Tooltip as RechartsTooltip,
|
||||
TooltipProps,
|
||||
XAxis,
|
||||
@@ -27,8 +26,11 @@ export default function AreaChart({
|
||||
color,
|
||||
data,
|
||||
grid,
|
||||
height,
|
||||
scale,
|
||||
showDecimals,
|
||||
stackBy,
|
||||
width,
|
||||
xAxisTitle,
|
||||
xKey,
|
||||
yAxisTitle,
|
||||
@@ -39,60 +41,60 @@ export default function AreaChart({
|
||||
}, [data, stackBy, xKey]);
|
||||
|
||||
return (
|
||||
<ResponsiveContainer>
|
||||
<RechartsAreaChart
|
||||
data={stackBy ? data.formattedData : data}
|
||||
margin={{ bottom: xAxisTitle ? 15 : 0, left: 5 }}
|
||||
>
|
||||
{grid && <CartesianGrid strokeDasharray='3 3' />}
|
||||
<RechartsTooltip
|
||||
content={(props: TooltipProps<number, string>) => (
|
||||
<Tooltip {...props} xKey={xKey} />
|
||||
)}
|
||||
wrapperStyle={{ outline: 'none', zIndex: 100 }}
|
||||
/>
|
||||
{stackBy ? (
|
||||
data.keys?.map((key: string, index: number) => (
|
||||
<Area
|
||||
activeDot={{ r: 2, strokeWidth: 1 }}
|
||||
dataKey={key}
|
||||
fill={PALETTE[index % PALETTE.length]}
|
||||
fillOpacity={0.75}
|
||||
key={key}
|
||||
stackId={'1'}
|
||||
stroke={PALETTE[index % PALETTE.length]}
|
||||
type='linear'
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Area
|
||||
activeDot={{ r: 5, strokeWidth: 0.5 }}
|
||||
dataKey={yKey}
|
||||
dot={false}
|
||||
fill={color}
|
||||
fillOpacity={0.6}
|
||||
isAnimationActive={false}
|
||||
stroke={color}
|
||||
type='monotone'
|
||||
/>
|
||||
<RechartsAreaChart
|
||||
data={stackBy ? data.formattedData : data}
|
||||
height={height}
|
||||
margin={{ bottom: xAxisTitle ? 30 : 0, left: 5 }}
|
||||
width={width}
|
||||
>
|
||||
{grid && <CartesianGrid strokeDasharray='3 3' />}
|
||||
<RechartsTooltip
|
||||
content={(props: TooltipProps<number, string>) => (
|
||||
<Tooltip {...props} showDecimals={showDecimals} xKey={xKey} />
|
||||
)}
|
||||
<XAxis
|
||||
dataKey={(x) => handleXKey(x, xKey)}
|
||||
label={X_AXIS_LABEL_INSTRUCTIONS(xAxisTitle ?? '')}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
tickFormatter={(tick) =>
|
||||
timeframe ? moment(tick).format(timeframe) : tick
|
||||
}
|
||||
wrapperStyle={{ outline: 'none', zIndex: 100 }}
|
||||
/>
|
||||
{stackBy ? (
|
||||
data.keys?.map((key: string, index: number) => (
|
||||
<Area
|
||||
activeDot={{ r: 2, strokeWidth: 1 }}
|
||||
dataKey={key}
|
||||
fill={PALETTE[index % PALETTE.length]}
|
||||
fillOpacity={0.75}
|
||||
key={key}
|
||||
stackId={'1'}
|
||||
stroke={PALETTE[index % PALETTE.length]}
|
||||
type='linear'
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Area
|
||||
activeDot={{ r: 5, strokeWidth: 0.5 }}
|
||||
dataKey={yKey}
|
||||
dot={false}
|
||||
fill={color}
|
||||
fillOpacity={0.6}
|
||||
isAnimationActive={false}
|
||||
stroke={color}
|
||||
type='monotone'
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={stackBy ? '' : yKey}
|
||||
domain={[0, 'auto']}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={Y_AXIS_LABEL_INSTRUCTIONS(yAxisTitle ?? '')}
|
||||
tickFormatter={(tick) => formatLargeNumber(tick)}
|
||||
scale={scale}
|
||||
/>
|
||||
</RechartsAreaChart>
|
||||
</ResponsiveContainer>
|
||||
)}
|
||||
<XAxis
|
||||
dataKey={(x) => handleXKey(x, xKey)}
|
||||
label={X_AXIS_LABEL_INSTRUCTIONS(xAxisTitle ?? '')}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
tickFormatter={(tick) =>
|
||||
timeframe ? moment(tick).format(timeframe) : tick
|
||||
}
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={stackBy ? '' : yKey}
|
||||
domain={[0, 'auto']}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={Y_AXIS_LABEL_INSTRUCTIONS(yAxisTitle ?? '')}
|
||||
tickFormatter={(tick) => formatLargeNumber(tick)}
|
||||
scale={scale}
|
||||
/>
|
||||
</RechartsAreaChart>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Bar,
|
||||
BarChart as RechartsBarChart,
|
||||
CartesianGrid,
|
||||
ResponsiveContainer,
|
||||
Tooltip as RechartsTooltip,
|
||||
TooltipProps,
|
||||
XAxis,
|
||||
@@ -27,8 +26,11 @@ export default function BarChart({
|
||||
color,
|
||||
data,
|
||||
grid,
|
||||
height,
|
||||
scale,
|
||||
showDecimals,
|
||||
stackBy,
|
||||
width,
|
||||
xAxisTitle,
|
||||
xKey,
|
||||
yAxisTitle,
|
||||
@@ -39,52 +41,52 @@ export default function BarChart({
|
||||
}, [data, stackBy, xKey]);
|
||||
|
||||
return (
|
||||
<ResponsiveContainer>
|
||||
<RechartsBarChart
|
||||
data={stackBy ? data.formattedData : data}
|
||||
margin={{ bottom: xAxisTitle ? 15 : 0, left: 5 }}
|
||||
>
|
||||
{grid && <CartesianGrid strokeDasharray='3 3' />}
|
||||
<RechartsTooltip
|
||||
content={(props: TooltipProps<number, string>) => (
|
||||
<Tooltip {...props} xKey={xKey} />
|
||||
)}
|
||||
wrapperStyle={{ outline: 'none', zIndex: 100 }}
|
||||
/>
|
||||
{stackBy ? (
|
||||
data.keys?.map((key: string, index: number) => (
|
||||
<Bar
|
||||
dataKey={key}
|
||||
fill={PALETTE[index % PALETTE.length]}
|
||||
fillOpacity={1}
|
||||
isAnimationActive={false}
|
||||
key={key}
|
||||
stackId={'1'}
|
||||
type='monotone'
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Bar dataKey={yKey} fill={color} isAnimationActive={false} />
|
||||
<RechartsBarChart
|
||||
data={stackBy ? data.formattedData : data}
|
||||
height={height}
|
||||
margin={{ bottom: xAxisTitle ? 30 : 0, left: 5 }}
|
||||
width={width}
|
||||
>
|
||||
{grid && <CartesianGrid strokeDasharray='3 3' />}
|
||||
<RechartsTooltip
|
||||
content={(props: TooltipProps<number, string>) => (
|
||||
<Tooltip {...props} showDecimals={showDecimals} xKey={xKey} />
|
||||
)}
|
||||
<XAxis
|
||||
dataKey={(x) => handleXKey(x, xKey)}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={X_AXIS_LABEL_INSTRUCTIONS(xAxisTitle)}
|
||||
name={xAxisTitle}
|
||||
tickFormatter={(tick) =>
|
||||
timeframe ? moment(tick).format(timeframe) : tick
|
||||
}
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={stackBy ? '' : yKey}
|
||||
domain={[0, 'auto']}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={Y_AXIS_LABEL_INSTRUCTIONS(yAxisTitle ?? '')}
|
||||
name={yAxisTitle}
|
||||
scale={scale}
|
||||
tickFormatter={(tick) => formatLargeNumber(tick)}
|
||||
/>
|
||||
</RechartsBarChart>
|
||||
</ResponsiveContainer>
|
||||
wrapperStyle={{ outline: 'none', zIndex: 100 }}
|
||||
/>
|
||||
{stackBy ? (
|
||||
data.keys?.map((key: string, index: number) => (
|
||||
<Bar
|
||||
dataKey={key}
|
||||
fill={PALETTE[index % PALETTE.length]}
|
||||
fillOpacity={1}
|
||||
isAnimationActive={false}
|
||||
key={key}
|
||||
stackId={'1'}
|
||||
type='monotone'
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Bar dataKey={yKey} fill={color} isAnimationActive={false} />
|
||||
)}
|
||||
<XAxis
|
||||
dataKey={(x) => handleXKey(x, xKey)}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={X_AXIS_LABEL_INSTRUCTIONS(xAxisTitle)}
|
||||
name={xAxisTitle}
|
||||
tickFormatter={(tick) =>
|
||||
timeframe ? moment(tick).format(timeframe) : tick
|
||||
}
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={stackBy ? '' : yKey}
|
||||
domain={[0, 'auto']}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={Y_AXIS_LABEL_INSTRUCTIONS(yAxisTitle ?? '')}
|
||||
name={yAxisTitle}
|
||||
scale={scale}
|
||||
tickFormatter={(tick) => formatLargeNumber(tick)}
|
||||
/>
|
||||
</RechartsBarChart>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useMemo } from 'react';
|
||||
import { ChartScale, ChartType, StackBy } from 'utils/constants';
|
||||
import PieChart from '../PieChart';
|
||||
import { generateStackData } from 'utils';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
type ChartWrapperProps = {
|
||||
chartType: string;
|
||||
@@ -13,8 +14,9 @@ type ChartWrapperProps = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: any[];
|
||||
grid?: boolean;
|
||||
stackBy?: StackBy;
|
||||
scale?: ChartScale;
|
||||
showDecimals?: boolean;
|
||||
stackBy?: StackBy;
|
||||
xAxisTitle: string;
|
||||
xKey: string;
|
||||
yAxisTitle: string;
|
||||
@@ -27,6 +29,7 @@ export default function ChartWrapper({
|
||||
data,
|
||||
grid,
|
||||
scale = ChartScale.Linear,
|
||||
showDecimals,
|
||||
stackBy,
|
||||
xAxisTitle,
|
||||
xKey,
|
||||
@@ -67,48 +70,82 @@ export default function ChartWrapper({
|
||||
|
||||
if (chartType === ChartType.Area) {
|
||||
return (
|
||||
<AreaChart
|
||||
color={color}
|
||||
data={sortedData}
|
||||
grid={!!grid}
|
||||
scale={scale}
|
||||
stackBy={stackBy}
|
||||
xAxisTitle={xAxisTitle}
|
||||
xKey={xKey || 'x'}
|
||||
yAxisTitle={yAxisTitle}
|
||||
yKey={yKey || 'y'}
|
||||
/>
|
||||
<AutoSizer>
|
||||
{({ height, width }: { height: number; width: number }) => (
|
||||
<AreaChart
|
||||
color={color}
|
||||
data={sortedData}
|
||||
grid={!!grid}
|
||||
height={height}
|
||||
scale={scale}
|
||||
showDecimals={showDecimals}
|
||||
stackBy={stackBy}
|
||||
width={width}
|
||||
xAxisTitle={xAxisTitle}
|
||||
xKey={xKey || 'x'}
|
||||
yAxisTitle={yAxisTitle}
|
||||
yKey={yKey || 'y'}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
} else if (chartType === ChartType.Bar) {
|
||||
return (
|
||||
<BarChart
|
||||
color={color}
|
||||
data={sortedData}
|
||||
grid={!!grid}
|
||||
scale={scale}
|
||||
stackBy={stackBy}
|
||||
xAxisTitle={xAxisTitle}
|
||||
xKey={xKey || 'x'}
|
||||
yAxisTitle={yAxisTitle}
|
||||
yKey={yKey || 'y'}
|
||||
/>
|
||||
<AutoSizer>
|
||||
{({ height, width }: { height: number; width: number }) => (
|
||||
<BarChart
|
||||
color={color}
|
||||
data={sortedData}
|
||||
grid={!!grid}
|
||||
height={height}
|
||||
scale={scale}
|
||||
showDecimals={showDecimals}
|
||||
stackBy={stackBy}
|
||||
width={width}
|
||||
xAxisTitle={xAxisTitle}
|
||||
xKey={xKey || 'x'}
|
||||
yAxisTitle={yAxisTitle}
|
||||
yKey={yKey || 'y'}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
} else if (chartType === ChartType.Line) {
|
||||
return (
|
||||
<LineChart
|
||||
color={color}
|
||||
curveType='basis'
|
||||
data={sortedData}
|
||||
grid={!!grid}
|
||||
scale={scale}
|
||||
stackBy={stackBy}
|
||||
xAxisTitle={xAxisTitle}
|
||||
xKey={xKey || 'x'}
|
||||
yAxisTitle={yAxisTitle}
|
||||
yKey={yKey || 'y'}
|
||||
/>
|
||||
<AutoSizer>
|
||||
{({ height, width }: { height: number; width: number }) => (
|
||||
<LineChart
|
||||
color={color}
|
||||
curveType='basis'
|
||||
data={sortedData}
|
||||
grid={!!grid}
|
||||
height={height}
|
||||
scale={scale}
|
||||
showDecimals={showDecimals}
|
||||
stackBy={stackBy}
|
||||
width={width}
|
||||
xAxisTitle={xAxisTitle}
|
||||
xKey={xKey || 'x'}
|
||||
yAxisTitle={yAxisTitle}
|
||||
yKey={yKey || 'y'}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
} else {
|
||||
return <PieChart data={data} dataKey={yKey || 'y'} nameKey={xKey || 'x'} />;
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ height, width }: { height: number; width: number }) => (
|
||||
<PieChart
|
||||
data={data}
|
||||
dataKey={yKey || 'y'}
|
||||
height={height}
|
||||
nameKey={xKey || 'x'}
|
||||
showDecimals={showDecimals}
|
||||
width={width}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
CartesianGrid,
|
||||
Line,
|
||||
LineChart as RechartsLineChart,
|
||||
ResponsiveContainer,
|
||||
Tooltip as RechartsTooltip,
|
||||
TooltipProps,
|
||||
XAxis,
|
||||
@@ -25,11 +24,13 @@ type LineChartProps = AxisChartProps;
|
||||
|
||||
export default function LineChart({
|
||||
color,
|
||||
curveType,
|
||||
data,
|
||||
grid,
|
||||
height,
|
||||
scale,
|
||||
showDecimals,
|
||||
stackBy,
|
||||
width,
|
||||
xAxisTitle,
|
||||
xKey,
|
||||
yAxisTitle,
|
||||
@@ -39,59 +40,59 @@ export default function LineChart({
|
||||
return determineTimeframe(stackBy ? data.formattedData : data, xKey);
|
||||
}, [data, stackBy, xKey]);
|
||||
return (
|
||||
<ResponsiveContainer>
|
||||
<RechartsLineChart
|
||||
data={stackBy ? data.formattedData : data}
|
||||
margin={{ bottom: xAxisTitle ? 15 : 0, left: 5 }}
|
||||
>
|
||||
{grid && <CartesianGrid strokeDasharray='3 3' />}
|
||||
<RechartsTooltip
|
||||
content={(props: TooltipProps<number, string>) => (
|
||||
<Tooltip {...props} xKey={xKey} />
|
||||
)}
|
||||
wrapperStyle={{ outline: 'none', zIndex: 100 }}
|
||||
/>
|
||||
{stackBy ? (
|
||||
data.keys?.map((key: string, index: number) => (
|
||||
<Line
|
||||
activeDot={{ strokeWidth: 1 }}
|
||||
dataKey={key}
|
||||
dot={false}
|
||||
fill={PALETTE[index % PALETTE.length]}
|
||||
key={key}
|
||||
stroke={PALETTE[index % PALETTE.length]}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Line
|
||||
activeDot={{ r: 5, strokeWidth: 0.5 }}
|
||||
isAnimationActive={false}
|
||||
dataKey={yKey}
|
||||
dot={false}
|
||||
stroke={color}
|
||||
type='monotone'
|
||||
/>
|
||||
<RechartsLineChart
|
||||
data={stackBy ? data.formattedData : data}
|
||||
height={height}
|
||||
margin={{ bottom: xAxisTitle ? 30 : 0, left: 5 }}
|
||||
width={width}
|
||||
>
|
||||
{grid && <CartesianGrid strokeDasharray='3 3' />}
|
||||
<RechartsTooltip
|
||||
content={(props: TooltipProps<number, string>) => (
|
||||
<Tooltip {...props} showDecimals={showDecimals} xKey={xKey} />
|
||||
)}
|
||||
<XAxis
|
||||
dataKey={(x) => handleXKey(x, xKey)}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={X_AXIS_LABEL_INSTRUCTIONS(xAxisTitle)}
|
||||
name={xAxisTitle}
|
||||
tickFormatter={(tick) =>
|
||||
timeframe ? moment(tick).format(timeframe) : tick
|
||||
}
|
||||
wrapperStyle={{ outline: 'none', zIndex: 100 }}
|
||||
/>
|
||||
{stackBy ? (
|
||||
data.keys?.map((key: string, index: number) => (
|
||||
<Line
|
||||
activeDot={{ strokeWidth: 1 }}
|
||||
dataKey={key}
|
||||
dot={false}
|
||||
fill={PALETTE[index % PALETTE.length]}
|
||||
key={key}
|
||||
stroke={PALETTE[index % PALETTE.length]}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Line
|
||||
activeDot={{ r: 5, strokeWidth: 0.5 }}
|
||||
isAnimationActive={false}
|
||||
dataKey={yKey}
|
||||
dot={false}
|
||||
stroke={color}
|
||||
type='monotone'
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={stackBy ? '' : yKey}
|
||||
domain={[0, 'auto']}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={Y_AXIS_LABEL_INSTRUCTIONS(yAxisTitle ?? '')}
|
||||
scale={scale}
|
||||
name={yAxisTitle}
|
||||
tickFormatter={(tick) => formatLargeNumber(tick)}
|
||||
/>
|
||||
</RechartsLineChart>
|
||||
</ResponsiveContainer>
|
||||
)}
|
||||
<XAxis
|
||||
dataKey={(x) => handleXKey(x, xKey)}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={X_AXIS_LABEL_INSTRUCTIONS(xAxisTitle)}
|
||||
name={xAxisTitle}
|
||||
tickFormatter={(tick) =>
|
||||
timeframe ? moment(tick).format(timeframe) : tick
|
||||
}
|
||||
/>
|
||||
<YAxis
|
||||
dataKey={stackBy ? '' : yKey}
|
||||
domain={[0, 'auto']}
|
||||
fontSize={TICKMARK_FONT_SIZE}
|
||||
label={Y_AXIS_LABEL_INSTRUCTIONS(yAxisTitle ?? '')}
|
||||
scale={scale}
|
||||
name={yAxisTitle}
|
||||
tickFormatter={(tick) => formatLargeNumber(tick)}
|
||||
/>
|
||||
</RechartsLineChart>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
Cell,
|
||||
Pie,
|
||||
PieChart as RechartsPieChart,
|
||||
ResponsiveContainer,
|
||||
Sector,
|
||||
Tooltip as RechartsTooltip,
|
||||
TooltipProps,
|
||||
@@ -16,13 +15,19 @@ type PieChartProps = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: any[];
|
||||
dataKey: string;
|
||||
height: number;
|
||||
nameKey: string;
|
||||
showDecimals?: boolean;
|
||||
width: number;
|
||||
};
|
||||
|
||||
export default function PieChart({
|
||||
data,
|
||||
dataKey,
|
||||
height,
|
||||
nameKey,
|
||||
showDecimals,
|
||||
width,
|
||||
}: PieChartProps): JSX.Element {
|
||||
const [activeIndex, setActiveIndex] = useState(-1);
|
||||
|
||||
@@ -49,34 +54,34 @@ export default function PieChart({
|
||||
};
|
||||
|
||||
return (
|
||||
<ResponsiveContainer>
|
||||
<RechartsPieChart>
|
||||
<RechartsTooltip
|
||||
content={(props: TooltipProps<number, string>) => (
|
||||
<Tooltip {...props} xKey={nameKey} />
|
||||
)}
|
||||
wrapperStyle={{ outline: 'none', zIndex: 100 }}
|
||||
/>
|
||||
<Pie
|
||||
activeIndex={activeIndex}
|
||||
activeShape={activeShape}
|
||||
cx='50%'
|
||||
cy='50%'
|
||||
data={data}
|
||||
dataKey={dataKey}
|
||||
labelLine={false}
|
||||
nameKey={nameKey}
|
||||
onMouseEnter={onPieEnter}
|
||||
onMouseLeave={() => setActiveIndex(-1)}
|
||||
>
|
||||
{data.map((_, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={PALETTE[index % PALETTE.length]}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
</RechartsPieChart>
|
||||
</ResponsiveContainer>
|
||||
<RechartsPieChart height={height * 0.9} width={width}>
|
||||
<RechartsTooltip
|
||||
content={(props: TooltipProps<number, string>) => (
|
||||
<Tooltip
|
||||
{...props}
|
||||
isPie={true}
|
||||
showDecimals={showDecimals}
|
||||
xKey={nameKey}
|
||||
/>
|
||||
)}
|
||||
wrapperStyle={{ outline: 'none', zIndex: 100 }}
|
||||
/>
|
||||
<Pie
|
||||
activeIndex={activeIndex}
|
||||
activeShape={activeShape}
|
||||
cx='50%'
|
||||
cy='50%'
|
||||
data={data}
|
||||
dataKey={dataKey}
|
||||
labelLine={false}
|
||||
nameKey={nameKey}
|
||||
onMouseEnter={onPieEnter}
|
||||
onMouseLeave={() => setActiveIndex(-1)}
|
||||
>
|
||||
{data.map((_, index) => (
|
||||
<Cell key={`cell-${index}`} fill={PALETTE[index % PALETTE.length]} />
|
||||
))}
|
||||
</Pie>
|
||||
</RechartsPieChart>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,28 +14,53 @@ const useStyles = createUseStyles({
|
||||
},
|
||||
});
|
||||
|
||||
type TooltipProps = { xKey: string } & RechartsTooltipProps<number, string>;
|
||||
type TooltipProps = {
|
||||
isPie?: boolean;
|
||||
showDecimals?: boolean;
|
||||
xKey: string;
|
||||
} & RechartsTooltipProps<number, string>;
|
||||
|
||||
export default function Tooltip(props: TooltipProps): JSX.Element {
|
||||
const { label, payload, xKey } = props;
|
||||
const { isPie, label, payload, showDecimals, xKey } = props;
|
||||
const styles = useStyles();
|
||||
if (!payload) return <></>;
|
||||
if (!payload || payload[0] === undefined) return <></>;
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Flex key={xKey} gap='12px' style={{ color: '#FCFCFC' }}>
|
||||
<div>{xKey}:</div>
|
||||
<div>
|
||||
{isDateOrTimestamp(label)
|
||||
? moment(label).format('MMM Do, YYYY')
|
||||
: label}
|
||||
</div>
|
||||
</Flex>
|
||||
{payload.map(({ color, name, payload: { fill }, value }) => (
|
||||
<Flex key={name} gap='12px' style={{ color: color ?? fill }}>
|
||||
<div>{name}:</div>
|
||||
<div>{formatNumber(value ?? 0, 0)}</div>
|
||||
</Flex>
|
||||
))}
|
||||
{isPie ? (
|
||||
<>
|
||||
<Flex key={xKey} gap='12px' style={{ color: '#FCFCFC' }}>
|
||||
<div>{xKey}:</div>
|
||||
<div>
|
||||
{isDateOrTimestamp(payload[0].name)
|
||||
? moment(payload[0].name).format('MMM Do, YYYY')
|
||||
: payload[0].name}
|
||||
</div>
|
||||
</Flex>
|
||||
<Flex gap='12px' style={{ color: payload[0].payload.fill }}>
|
||||
<div>{payload[0].dataKey}:</div>
|
||||
<div>
|
||||
{formatNumber(payload[0].value ?? 0, showDecimals ? 2 : 0)}
|
||||
</div>
|
||||
</Flex>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Flex key={xKey} gap='12px' style={{ color: '#FCFCFC' }}>
|
||||
<div>{xKey}:</div>
|
||||
<div>
|
||||
{isDateOrTimestamp(label)
|
||||
? moment(label).format('MMM Do, YYYY')
|
||||
: label}
|
||||
</div>
|
||||
</Flex>
|
||||
{payload.map(({ color, name, payload: { fill }, value }) => (
|
||||
<Flex key={name} gap='12px' style={{ color: color ?? fill }}>
|
||||
<div>{name}:</div>
|
||||
<div>{formatNumber(value ?? 0, showDecimals ? 2 : 0)}</div>
|
||||
</Flex>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@ type CounterProps = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: any;
|
||||
label: string;
|
||||
showDecimal?: boolean;
|
||||
};
|
||||
|
||||
export default function Counter({
|
||||
counterKey,
|
||||
data,
|
||||
label,
|
||||
showDecimal,
|
||||
}: CounterProps): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
@@ -23,9 +25,9 @@ export default function Counter({
|
||||
style={{ color: '#FCFCFC' }}
|
||||
>
|
||||
<Flex direction='column' alignItems='center'>
|
||||
<Typography variant='subtitle1'>{label}</Typography>
|
||||
<Typography variant='h6'>{label}</Typography>
|
||||
<Typography variant='h4'>
|
||||
{formatNumber(data[0][counterKey], 0)}
|
||||
{formatNumber(data[0][counterKey], showDecimal ? 2 : 0)}
|
||||
</Typography>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
98
src/data.ts
98
src/data.ts
@@ -3,13 +3,13 @@ import { ChartType } from "utils/constants";
|
||||
export const COUNTERS = [
|
||||
{
|
||||
counterKey: 'Num_Wallets_Created',
|
||||
label: 'Wallets Created',
|
||||
label: 'BLS Wallets Created',
|
||||
queryId: 'aa55c0623a28438eac9aecfb7b767726',
|
||||
title: 'Wallets'
|
||||
},
|
||||
{
|
||||
counterKey: 'Num_Wallets_Recovered',
|
||||
label: 'Wallets Recovered',
|
||||
label: 'BLS Wallets Recovered',
|
||||
queryId: 'aa55c0623a28438eac9aecfb7b767726',
|
||||
title: 'Wallets'
|
||||
},
|
||||
@@ -19,6 +19,12 @@ export const COUNTERS = [
|
||||
queryId: 'aa55c0623a28438eac9aecfb7b767726',
|
||||
title: 'Tx Groups'
|
||||
},
|
||||
{
|
||||
counterKey: 'Num_Operations_Submitted',
|
||||
label: 'Operations Submitted',
|
||||
queryId: 'aa55c0623a28438eac9aecfb7b767726',
|
||||
title: 'Tx Groups'
|
||||
},
|
||||
{
|
||||
counterKey: 'Num_Operations_Failed',
|
||||
label: 'Operations Failed',
|
||||
@@ -31,28 +37,25 @@ export const COUNTERS = [
|
||||
queryId: 'aa55c0623a28438eac9aecfb7b767726',
|
||||
title: 'Tx Groups'
|
||||
},
|
||||
{
|
||||
counterKey: 'Num_Operations_Submitted',
|
||||
label: 'Operations Submitted',
|
||||
queryId: 'aa55c0623a28438eac9aecfb7b767726',
|
||||
title: 'Tx Groups'
|
||||
},
|
||||
{
|
||||
counterKey: 'Avg_Operations_Per_Bundle',
|
||||
label: 'Operations/Bundle',
|
||||
queryId: 'aa55c0623a28438eac9aecfb7b767726',
|
||||
showDecimal: true,
|
||||
title: 'Averages'
|
||||
},
|
||||
{
|
||||
counterKey: 'Avg_Actions_Per_Bundle',
|
||||
label: 'Actions/Bundle',
|
||||
queryId: 'aa55c0623a28438eac9aecfb7b767726',
|
||||
showDecimal: true,
|
||||
title: 'Averages'
|
||||
},
|
||||
{
|
||||
counterKey: 'Avg_Actions_Per_Operation',
|
||||
label: 'Actions/Operations',
|
||||
queryId: 'aa55c0623a28438eac9aecfb7b767726',
|
||||
showDecimal: true,
|
||||
title: 'Averages'
|
||||
},
|
||||
{
|
||||
@@ -76,20 +79,6 @@ export const COUNTERS = [
|
||||
];
|
||||
|
||||
export const CHARTS = [
|
||||
{
|
||||
chartType: ChartType.Pie,
|
||||
queryId: '7ad7fb1828094272880202e903af4239',
|
||||
title: 'Action Method Ids Called',
|
||||
xKey: 'actionMethodId',
|
||||
yKey: 'action_count'
|
||||
},
|
||||
{
|
||||
chartType: ChartType.Pie,
|
||||
queryId: '2578dd88f123458183e0fe077d285243',
|
||||
title: 'Action Recipients',
|
||||
xKey: 'actionsRecipient',
|
||||
yKey: 'action_count'
|
||||
},
|
||||
{
|
||||
chartType: ChartType.Bar,
|
||||
color: '#B8B7D0',
|
||||
@@ -113,6 +102,7 @@ export const CHARTS = [
|
||||
{
|
||||
chartType: ChartType.Bar,
|
||||
color: '#CE83D9',
|
||||
description: 'Number of bundles (a group of operations containing actions) submitted on-chain',
|
||||
queryId: 'ab1a30af5991425b8b4abd7c37f859fa',
|
||||
title: 'Number of Bundles Submitted per Day',
|
||||
xAxisTitle: 'Day',
|
||||
@@ -123,6 +113,7 @@ export const CHARTS = [
|
||||
{
|
||||
chartType: ChartType.Bar,
|
||||
color: '#8784D8',
|
||||
description: 'Number of Actions (function calls coming from a signed operation) submitted on-chain',
|
||||
queryId: 'ab1a30af5991425b8b4abd7c37f859fa',
|
||||
title: 'Number of Actions Submitted per Day',
|
||||
xAxisTitle: 'Day',
|
||||
@@ -133,6 +124,7 @@ export const CHARTS = [
|
||||
{
|
||||
chartType: ChartType.Bar,
|
||||
color: '#C6C5CF',
|
||||
description: 'Number of Operations (a group of actions) submitted on-chain',
|
||||
queryId: 'ab1a30af5991425b8b4abd7c37f859fa',
|
||||
title: 'Number of Operations Submitted per Day',
|
||||
xAxisTitle: 'Day',
|
||||
@@ -154,6 +146,7 @@ export const CHARTS = [
|
||||
chartType: ChartType.Bar,
|
||||
color: '#84D9CC',
|
||||
queryId: 'ab1a30af5991425b8b4abd7c37f859fa',
|
||||
showDecimals: true,
|
||||
title: 'Average Operations per Bundle per day',
|
||||
xAxisTitle: 'Day',
|
||||
xKey: 'day',
|
||||
@@ -164,6 +157,7 @@ export const CHARTS = [
|
||||
chartType: ChartType.Bar,
|
||||
color: '#8784D8',
|
||||
queryId: 'ab1a30af5991425b8b4abd7c37f859fa',
|
||||
showDecimals: true,
|
||||
title: 'Average Actions per Bundle Per Dayy',
|
||||
xAxisTitle: 'Day',
|
||||
xKey: 'day',
|
||||
@@ -174,6 +168,7 @@ export const CHARTS = [
|
||||
chartType: ChartType.Bar,
|
||||
color: '#B6B6BF',
|
||||
queryId: 'ab1a30af5991425b8b4abd7c37f859fa',
|
||||
showDecimals: true,
|
||||
title: 'Average Actions per Operation per Day',
|
||||
xAxisTitle: 'Day',
|
||||
xKey: 'day',
|
||||
@@ -210,6 +205,20 @@ export const CHARTS = [
|
||||
yAxisTitle: 'Average Gas per Transaction',
|
||||
yKey: 'avgGas'
|
||||
},
|
||||
{
|
||||
chartType: ChartType.Pie,
|
||||
queryId: '7ad7fb1828094272880202e903af4239',
|
||||
title: 'Action Method Ids Called',
|
||||
xKey: 'actionMethodId',
|
||||
yKey: 'action_count'
|
||||
},
|
||||
{
|
||||
chartType: ChartType.Pie,
|
||||
queryId: '2578dd88f123458183e0fe077d285243',
|
||||
title: 'Action Recipients',
|
||||
xKey: 'actionsRecipient',
|
||||
yKey: 'action_count'
|
||||
},
|
||||
{
|
||||
chartType: ChartType.Bar,
|
||||
queryId: '74a8783fd397441ca0dd9cbc96423e81',
|
||||
@@ -236,16 +245,35 @@ export const CHARTS = [
|
||||
},
|
||||
];
|
||||
|
||||
// type ChartWrapperProps = {
|
||||
// chartType: string;
|
||||
// color: string;
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// data: any[];
|
||||
// grid?: boolean;
|
||||
// stackBy?: StackBy;
|
||||
// scale?: ChartScale;
|
||||
// xAxisTitle: string;
|
||||
// xKey: string;
|
||||
// yAxisTitle: string;
|
||||
// yKey: string;
|
||||
// };
|
||||
export const SECTIONS = [
|
||||
{
|
||||
charts: [0, 2],
|
||||
counters: [0, 2],
|
||||
description: 'BLS smart-contract wallets for use with WAX',
|
||||
title: 'Wallets',
|
||||
},
|
||||
{
|
||||
charts: [2, 6],
|
||||
counters: [2, 6],
|
||||
description: 'Bundles, submitted on-chain, encompass Operations that carry Actions - the actual function calls.',
|
||||
title: 'Bundle Composition'
|
||||
},
|
||||
{
|
||||
charts: [6, 9],
|
||||
counters: [6, 9],
|
||||
description: 'Bundle, Operations, & Actions Relationship',
|
||||
title: 'Averages',
|
||||
},
|
||||
{
|
||||
charts: [9, 12],
|
||||
counters: [9, 12],
|
||||
description: 'The gas expenses for each bundle submission.',
|
||||
title: 'Gas',
|
||||
},
|
||||
{
|
||||
charts: [12],
|
||||
counters: [],
|
||||
description: 'Metrics related to the contracts called and method IDs invoked within BLS Wallet actions.',
|
||||
title: 'Actions'
|
||||
}
|
||||
];
|
||||
@@ -18,7 +18,7 @@
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #262726;
|
||||
background-color: #090213;
|
||||
margin: 0;
|
||||
font-family: 'Heebo', 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CSSProperties } from 'react';
|
||||
|
||||
export type TypographyVariant =
|
||||
| 'caption'
|
||||
| 'h2'
|
||||
| 'h4'
|
||||
| 'h5'
|
||||
| 'h6'
|
||||
@@ -21,6 +22,13 @@ export const typography: TypographyVariantMap = {
|
||||
fontWeight: 400,
|
||||
lineHeight: '14px',
|
||||
},
|
||||
h2: {
|
||||
fontFamily: 'Heebo',
|
||||
fontSize: '48px',
|
||||
fontWeight: 500,
|
||||
letterSpacing: '0.5px',
|
||||
lineHeight: 'g6px',
|
||||
},
|
||||
h4: {
|
||||
fontFamily: 'Heebo',
|
||||
fontSize: '34px',
|
||||
|
||||
@@ -8,6 +8,7 @@ export type AxisChartProps = {
|
||||
grid: boolean;
|
||||
height?: number;
|
||||
scale: ChartScale;
|
||||
showDecimals?: boolean;
|
||||
stackBy?: StackBy;
|
||||
width?: number;
|
||||
xAxisTitle: string;
|
||||
@@ -35,18 +36,12 @@ export const MS_PER_MONTH = 30.44 * MS_PER_DAY; // Milleseconds per month
|
||||
export const MS_PER_YEAR = 365.25 * MS_PER_DAY; // Millesconds per year
|
||||
|
||||
export const PALETTE = [
|
||||
'#a6cee3',
|
||||
'#1f78b4',
|
||||
'#b2df8a',
|
||||
'#33a02c',
|
||||
'#fb9a99',
|
||||
'#e31a1c',
|
||||
'#fdbf6f',
|
||||
'#ff7f00',
|
||||
'#cab2d6',
|
||||
'#6a3d9a',
|
||||
'#ffff99',
|
||||
'#b15928',
|
||||
'#84ACCE',
|
||||
'#74FFCE',
|
||||
'#E040FB',
|
||||
'#3F51B5',
|
||||
'#F18F01'
|
||||
|
||||
];
|
||||
|
||||
export type StackBy = {
|
||||
|
||||
@@ -8087,6 +8087,11 @@ react-transition-group@2.9.0:
|
||||
prop-types "^15.6.2"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
react-virtualized-auto-sizer@^1.0.20:
|
||||
version "1.0.20"
|
||||
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.20.tgz#d9a907253a7c221c52fa57dc775a6ef40c182645"
|
||||
integrity sha512-OdIyHwj4S4wyhbKHOKM1wLSj/UDXm839Z3Cvfg2a9j+He6yDa6i5p0qQvEiCnyQlGO/HyfSnigQwuxvYalaAXA==
|
||||
|
||||
react@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||
|
||||
Reference in New Issue
Block a user