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

Feat home screen charts #1112

Merged
merged 18 commits into from
Feb 21, 2025
Merged
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
59 changes: 59 additions & 0 deletions explorer/gql/graphql.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22148,6 +22148,11 @@ export type HomeQueryVariables = Exact<{

export type HomeQuery = { __typename?: 'query_root', consensus_blocks: Array<{ __typename?: 'consensus_blocks', id: string, height: any, timestamp: any, extrinsics_count: number, events_count: number, space_pledged: any, blockchain_size: any, extrinsicsCount: number, extrinsics: Array<{ __typename?: 'consensus_extrinsics', id: string, hash: string, block_height: any, name: string, timestamp: any, success: boolean }>, cumulative?: { __typename?: 'consensus_cumulative_blocks', cumulative_extrinsics_count: any, cumulative_events_count: any, cumulative_transfers_count: any, cumulative_transfer_value: any, cumulative_rewards_count: any, cumulative_reward_value: any } | null }>, consensus_accounts_aggregate: { __typename?: 'consensus_accounts_aggregate', aggregate?: { __typename?: 'consensus_accounts_aggregate_fields', count: number } | null } };

export type OnChainActivityChartsQueryVariables = Exact<{ [key: string]: never; }>;


export type OnChainActivityChartsQuery = { __typename?: 'query_root', stats_daily: Array<{ __typename?: 'stats_daily', cumulated_history_size: any, delta_history_size: any, start_date: any, end_date: any }>, stats_weekly: Array<{ __typename?: 'stats_weekly', cumulated_history_size: any, delta_history_size: any, start_date: any, end_date: any }>, stats_monthly: Array<{ __typename?: 'stats_monthly', cumulated_history_size: any, delta_history_size: any, start_date: any, end_date: any }> };

export type LogsQueryVariables = Exact<{
limit: Scalars['Int']['input'];
offset?: InputMaybe<Scalars['Int']['input']>;
Expand Down Expand Up @@ -23626,6 +23631,60 @@ export type HomeQueryHookResult = ReturnType<typeof useHomeQuery>;
export type HomeLazyQueryHookResult = ReturnType<typeof useHomeLazyQuery>;
export type HomeSuspenseQueryHookResult = ReturnType<typeof useHomeSuspenseQuery>;
export type HomeQueryResult = Apollo.QueryResult<HomeQuery, HomeQueryVariables>;
export const OnChainActivityChartsDocument = gql`
query OnChainActivityCharts {
stats_daily(limit: 30, offset: 0, order_by: {end_block: desc}) {
cumulated_history_size
delta_history_size
start_date
end_date
}
stats_weekly(limit: 30, offset: 0, order_by: {end_block: desc}) {
cumulated_history_size
delta_history_size
start_date
end_date
}
stats_monthly(limit: 30, offset: 0, order_by: {end_block: desc}) {
cumulated_history_size
delta_history_size
start_date
end_date
}
}
`;

/**
* __useOnChainActivityChartsQuery__
*
* To run a query within a React component, call `useOnChainActivityChartsQuery` and pass it any options that fit your needs.
* When your component renders, `useOnChainActivityChartsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useOnChainActivityChartsQuery({
* variables: {
* },
* });
*/
export function useOnChainActivityChartsQuery(baseOptions?: Apollo.QueryHookOptions<OnChainActivityChartsQuery, OnChainActivityChartsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<OnChainActivityChartsQuery, OnChainActivityChartsQueryVariables>(OnChainActivityChartsDocument, options);
}
export function useOnChainActivityChartsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<OnChainActivityChartsQuery, OnChainActivityChartsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<OnChainActivityChartsQuery, OnChainActivityChartsQueryVariables>(OnChainActivityChartsDocument, options);
}
export function useOnChainActivityChartsSuspenseQuery(baseOptions?: Apollo.SkipToken | Apollo.SuspenseQueryHookOptions<OnChainActivityChartsQuery, OnChainActivityChartsQueryVariables>) {
const options = baseOptions === Apollo.skipToken ? baseOptions : {...defaultOptions, ...baseOptions}
return Apollo.useSuspenseQuery<OnChainActivityChartsQuery, OnChainActivityChartsQueryVariables>(OnChainActivityChartsDocument, options);
}
export type OnChainActivityChartsQueryHookResult = ReturnType<typeof useOnChainActivityChartsQuery>;
export type OnChainActivityChartsLazyQueryHookResult = ReturnType<typeof useOnChainActivityChartsLazyQuery>;
export type OnChainActivityChartsSuspenseQueryHookResult = ReturnType<typeof useOnChainActivityChartsSuspenseQuery>;
export type OnChainActivityChartsQueryResult = Apollo.QueryResult<OnChainActivityChartsQuery, OnChainActivityChartsQueryVariables>;
export const LogsDocument = gql`
query Logs($limit: Int!, $offset: Int, $orderBy: [consensus_logs_order_by!]!, $where: consensus_logs_bool_exp) {
consensus_logs_aggregate(where: $where) {
Expand Down
9 changes: 6 additions & 3 deletions explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@
"@heroicons/react": "^2.1.1",
"@netlify/edge-functions": "^2.11.1",
"@next/third-parties": "^14.2.3",
"@nivo/core": "^0.84.0",
"@nivo/line": "^0.84.0",
"@nivo/pie": "^0.84.0",
"@nivo/bar": "^0.88.0",
"@nivo/core": "^0.88.0",
"@nivo/line": "^0.88.0",
"@nivo/pie": "^0.88.0",
"@polkadot/extension-dapp": "^0.46.7",
"@polkadot/extension-inject": "^0.46.7",
"@polkadot/react-identicon": "^3.6.5",
Expand All @@ -55,6 +56,7 @@
"@testing-library/user-event": "^14.5.2",
"@vercel/og": "^0.6.2",
"clsx": "^2.1.1",
"d3": "^7.9.0",
"dayjs": "^1.11.10",
"ethers": "^6.11.1",
"faunadb": "^4.8.0",
Expand All @@ -79,6 +81,7 @@
"react-intersection-observer": "^9.13.0",
"react-json-view": "^1.21.3",
"react-paginate": "^8.2.0",
"react-spring": "^9.7.5",
"siwe": "^2.1.4",
"swiper": "^11.2.2",
"tailwind-datepicker-react": "^1.4.3",
Expand Down
7 changes: 7 additions & 0 deletions explorer/src/app/[chain]/stats/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ConsensusHeader } from 'components/layout/ConsensusHeader'
import { MainLayout } from 'components/layout/Layout'
import type { ChildrenPageProps } from 'types/app'

export default async function Layout({ children }: ChildrenPageProps) {
return <MainLayout subHeader={<ConsensusHeader />}>{children}</MainLayout>
}
12 changes: 12 additions & 0 deletions explorer/src/app/[chain]/stats/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Stats } from 'components/Stats'
import { Metadata } from 'next'
import { FC } from 'react'
import type { ChainPageProps } from 'types/app'
import { getMetadata } from 'utils/metadata/basic'

export const generateMetadata = ({ params: { chain } }: ChainPageProps): Metadata =>
getMetadata(chain, 'Stats', undefined)

const Page: FC<ChainPageProps> = () => <Stats />

export default Page
114 changes: 114 additions & 0 deletions explorer/src/components/Consensus/Home/AccumulatedHistoryChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
'use client'

import { formatSpaceToDecimalAsObject } from '@autonomys/auto-consensus'
import { LineChart, TimeFrame, TimeFrameSelector } from 'components/common/Charts'
import { OnChainActivityChartsQuery } from 'gql/graphql'
import { FC, useMemo, useState } from 'react'
import { formatDate, getLineChartTickValues } from 'utils/dateFormat'

interface AccumulatedHistoryChartProps {
data: OnChainActivityChartsQuery | undefined
loading: boolean
}

// Add these type definitions at the top of the file
type UnitType = 'B' | 'KB' | 'MB' | 'GB' | 'TB'
type UnitFactors = Record<UnitType, number>
type UnitOrder = Record<UnitType, number>

export const AccumulatedHistoryChart: FC<AccumulatedHistoryChartProps> = ({ data, loading }) => {
const [timeFrame, setTimeFrame] = useState<TimeFrame>('1D')

const singleTimeFrameData = useMemo(() => {
if (!data) return []

const statsTable = {
// '1H': data.stats_hourly,
'1D': data.stats_daily,
'1W': data.stats_weekly,
'1M': data.stats_monthly,
}[timeFrame]

// Find the largest unit among all data points
const allUnits = statsTable.map(
(d) => formatSpaceToDecimalAsObject(d.cumulated_history_size).unit as UnitType,
)
const largestUnit = allUnits.reduce((max, current) => {
const unitOrder: UnitOrder = { B: 1, KB: 2, MB: 3, GB: 4, TB: 5 }
return unitOrder[current] > unitOrder[max] ? current : max
}, 'B' as UnitType)

// Convert all values to the largest unit
const convertToUnit = (value: number, fromUnit: UnitType, toUnit: UnitType): number => {
const unitFactors: UnitFactors = {
B: 1,
KB: 1024,
MB: 1024 * 1024,
GB: 1024 * 1024 * 1024,
TB: 1024 * 1024 * 1024 * 1024,
}
return (value * unitFactors[fromUnit]) / unitFactors[toUnit]
}

return [
{
id: 'accumulated',
data: statsTable.map((d) => {
const space = formatSpaceToDecimalAsObject(d.cumulated_history_size)
return {
date: formatDate(new Date(d.start_date), timeFrame),
size: convertToUnit(space.value, space.unit as UnitType, largestUnit),
unit: largestUnit,
x: new Date(d.start_date), // Ensure x is properly typed for the chart
y: convertToUnit(space.value, space.unit as UnitType, largestUnit), // Ensure y is properly typed for the chart
}
}),
},
]
}, [data, timeFrame])

if (loading || !data) return <div>Loading...</div>

return (
<div className='rounded-[20px] bg-white p-4 shadow-sm dark:bg-boxDark'>
<div className='mb-4 flex items-center justify-between'>
<h2 className='text-lg font-medium dark:text-white'>Accumulated On-Chain Activity</h2>
<TimeFrameSelector selected={timeFrame} onChange={setTimeFrame} />
</div>
<div className='h-[300px]'>
<LineChart
data={singleTimeFrameData}
margin={{ top: 10, right: 20, bottom: 50, left: 60 }}
xScale={{ type: 'time', useUTC: false }}
yScale={{ type: 'linear', min: 'auto', max: 'auto' }}
axisLeft={{
tickSize: 5,
tickPadding: 5,
legend: `Size (${singleTimeFrameData[0]?.data[0]?.unit ?? 'GB'})`,
legendOffset: -50,
legendPosition: 'middle',
}}
axisBottom={{
tickSize: 5,
tickPadding: 10,
legend: 'Time',
legendOffset: 40,
legendPosition: 'middle',
format: (value) => formatDate(new Date(value), timeFrame),
tickValues: getLineChartTickValues(
timeFrame,
singleTimeFrameData[0].data.map((d) => ({ date: d.x.toISOString() })), // Convert `x` to `date`
),
}}
sliceTooltip={({ slice }) => (
<div className='rounded bg-gray-800 px-2 py-1 text-xs text-white'>
{formatDate(new Date(slice.points[0].data.x), timeFrame)}:{' '}
{Number(slice.points[0].data.y).toFixed(2)}$
{singleTimeFrameData[0]?.data[0]?.unit ?? 'GB'}
</div>
)}
/>
</div>
</div>
)
}
126 changes: 126 additions & 0 deletions explorer/src/components/Consensus/Home/HistoryIncreaseChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
'use client'

import { formatSpaceToDecimalAsObject } from '@autonomys/auto-consensus'
import { BarChart, TimeFrame, TimeFrameSelector } from 'components/common/Charts'
import { OnChainActivityChartsQuery } from 'gql/graphql'
import { FC, useMemo, useState } from 'react'
import { formatDate, getBarChartTickValues } from 'utils/dateFormat'

interface HistoryIncreaseChartProps {
data: OnChainActivityChartsQuery | undefined
loading: boolean
}

export const HistoryIncreaseChart: FC<HistoryIncreaseChartProps> = ({ data, loading }) => {
const [timeFrame, setTimeFrame] = useState<TimeFrame>('1D')

const singleTimeFrameData = useMemo(() => {
if (!data) return { data: [], unit: '' }

const statsTable = {
// '1H': data.stats_hourly,
'1D': data.stats_daily,
'1W': data.stats_weekly,
'1M': data.stats_monthly,
}[timeFrame]

// First map to get all units and values
const mappedData = statsTable.map((d) => {
const space = formatSpaceToDecimalAsObject(d.delta_history_size)
return {
date: formatDate(new Date(d.start_date), timeFrame),
increase: space.value || 0,
unit: space.unit || 'KB',
}
})

// Find the biggest unit
const units = mappedData.map((d) => d.unit)
const uniqueUnits = [...new Set(units)]

// If all units are the same, return as is
if (uniqueUnits.length === 1) {
return {
data: mappedData.reverse(),
unit: uniqueUnits[0],
}
}

// If different units, convert to the biggest one
// Assuming units can be 'KB', 'MB', 'GB', 'TB'
const unitConversion = {
KB: 1,
MB: 1024,
GB: 1024 * 1024,
TB: 1024 * 1024 * 1024,
}

const biggestUnit = uniqueUnits.reduce((a, b) =>
unitConversion[a as keyof typeof unitConversion] >
unitConversion[b as keyof typeof unitConversion]
? a
: b,
)

return {
data: mappedData
.map((d) => ({
...d,
increase:
d.unit === biggestUnit
? d.increase
: d.increase /
(unitConversion[biggestUnit as keyof typeof unitConversion] /
unitConversion[d.unit as keyof typeof unitConversion]),
}))
.reverse(),
unit: biggestUnit,
}
}, [data, timeFrame])

if (loading || !data) return <div>Loading...</div>

return (
<div className='rounded-[20px] bg-white p-4 shadow-sm dark:bg-boxDark'>
<div className='mb-4 flex items-center justify-between'>
<h2 className='text-lg font-medium dark:text-white'>On-Chain Activity Increase</h2>
<TimeFrameSelector selected={timeFrame} onChange={setTimeFrame} />
</div>
<div className='h-[300px]'>
<BarChart
data={singleTimeFrameData.data}
keys={['increase']}
indexBy='date'
margin={{ top: 10, right: 20, bottom: 50, left: 60 }}
valueScale={{ type: 'linear' }}
indexScale={{ type: 'band', round: true }}
axisLeft={{
tickSize: 5,
tickPadding: 5,
legend: `Increase (${singleTimeFrameData.unit})`,
legendOffset: -50,
legendPosition: 'middle',
}}
axisBottom={{
tickSize: 5,
tickPadding: 10,
legend: 'Time',
legendOffset: 40,
legendPosition: 'middle',
tickRotation: 0,
format: (value) => value,
tickValues: getBarChartTickValues(
timeFrame,
singleTimeFrameData.data.map((d) => ({ date: d.date })),
),
}}
tooltip={({ value, indexValue }) => (
<div className='rounded bg-gray-800 px-2 py-1 text-xs text-white'>
{indexValue}: {value.toFixed(3)} {singleTimeFrameData.unit}
</div>
)}
/>
</div>
</div>
)
}
22 changes: 22 additions & 0 deletions explorer/src/components/Consensus/Home/HomeCharts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useOnChainActivityChartsQuery } from 'gql/graphql'
import React from 'react'
import { useInView } from 'react-intersection-observer'
import { AccumulatedHistoryChart } from './AccumulatedHistoryChart'
import { HistoryIncreaseChart } from './HistoryIncreaseChart'

export const HomeCharts = () => {
const { ref, inView } = useInView()
const { loading, data } = useOnChainActivityChartsQuery({
variables: {},
skip: !inView,
})

return (
<div ref={ref}>
<div className='mb-12 mt-4 grid grid-cols-1 gap-5 lg:grid-cols-2'>
<AccumulatedHistoryChart data={data} loading={loading} />
<HistoryIncreaseChart data={data} loading={loading} />
</div>
</div>
)
}
Loading