Skip to content

Commit

Permalink
Merge pull request #80 from oceanprotocol/74-total-number-of-nodes-ch…
Browse files Browse the repository at this point in the history
…anges-when-searching

74 total number of nodes changes when searching
  • Loading branch information
bogdanfazakas authored Feb 25, 2025
2 parents 758e25c + 99028e2 commit 2744f67
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 157 deletions.
32 changes: 18 additions & 14 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
Area,
ResponsiveContainer,
XAxis,
Tooltip
Tooltip,
CartesianGrid
} from 'recharts'

interface CardProps {
Expand Down Expand Up @@ -132,16 +133,16 @@ const Card: React.FC<CardProps> = ({
</ResponsiveContainer>
)}
{chartType === 'line' && chartData && chartData.length > 0 && (
<ResponsiveContainer
width="100%"
height={200}
style={{ paddingTop: '50px' }}
>
<LineChart data={chartData}>
<ResponsiveContainer width="100%" height={170}>
<LineChart
data={chartData}
margin={{ top: 10, right: 0, bottom: 10, left: 0 }}
>
<defs>
<linearGradient id="lineWave" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#CF1FB1" stopOpacity={1} />
<stop offset="100%" stopColor="#CF1FB1" stopOpacity={0.2} />
<linearGradient id="lineWave" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stopColor="#7b1173" stopOpacity={0.8} />
<stop offset="30%" stopColor="#bd2881" stopOpacity={0.6} />
<stop offset="60%" stopColor="#CF1FB1" stopOpacity={1} />
</linearGradient>
<filter id="shadow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceAlpha" stdDeviation="4" />
Expand All @@ -155,6 +156,8 @@ const Card: React.FC<CardProps> = ({
</feMerge>
</filter>
</defs>
<CartesianGrid opacity={0} />
<XAxis hide />
<Line
type="basis"
dataKey="foreground.value"
Expand All @@ -169,7 +172,6 @@ const Card: React.FC<CardProps> = ({
stroke: '#ffffff',
strokeWidth: 2
}}
filter="url(#shadow)"
/>
<Area
type="basis"
Expand All @@ -181,17 +183,19 @@ const Card: React.FC<CardProps> = ({
<Tooltip
contentStyle={{
background: '#1A0820',
border: '1px solid #CF1FB1',
border: '1px solid rgba(207, 31, 177, 0.3)',
borderRadius: '8px',
boxShadow: '0 4px 20px rgba(207, 31, 177, 0.3)'
boxShadow: '0 4px 20px rgba(207, 31, 177, 0.3)',
cursor: 'none'
}}
cursor={false}
formatter={(value) => [
<span key="value" style={{ color: '#CF1FB1' }}>
{Number(value).toLocaleString()} ROSE
</span>
]}
labelFormatter={(label) => (
<span style={{ color: '#CF1FB1' }}>{label}</span>
<span style={{ color: '#CF1FB1' }}>Week {label}</span>
)}
/>
</LineChart>
Expand Down
55 changes: 42 additions & 13 deletions src/components/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import Card from '../Card/Card'
import styles from './Dashboard.module.css'
import { useDataContext } from '@/context/DataContext'
import { useMapContext } from '../../context/MapContext'
import { CircularProgress, Alert, Box } from '@mui/material'
import { Alert, Box } from '@mui/material'
import { usePathname } from 'next/navigation'

const formatNumber = (num: number | string | undefined): string => {
if (num === undefined) return '0'
const formatNumber = (num: number | string | null | undefined): string => {
if (num === null || num === undefined) return '-'
const numStr = typeof num === 'string' ? num : num.toString()
return numStr.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}

const formatRewardsNumber = (num: number | string | undefined): string => {
if (num === undefined) return '0'
const formatRewardsNumber = (num: number | string | null | undefined): string => {
if (num === null || num === undefined) return '-'
const number = typeof num === 'string' ? parseFloat(num) : num

if (number >= 1000000) {
Expand All @@ -29,11 +29,39 @@ const formatRewardsNumber = (num: number | string | undefined): string => {
}

const Dashboard = () => {
const { loading, error, totalNodes, totalEligibleNodes, totalRewards, rewardsHistory } =
useDataContext()
const {
loadingTotalNodes,
loadingTotalEligible,
loadingTotalRewards,
loadingRewardsHistory,
error,
totalNodes,
totalEligibleNodes,
totalRewards,
rewardsHistory
} = useDataContext()
const { totalCountries, loading: mapLoading } = useMapContext()
const pathname = usePathname()

const combinedLoading =
loadingTotalNodes ||
loadingTotalEligible ||
loadingTotalRewards ||
loadingRewardsHistory

const [overallDashboardLoading, setOverallDashboardLoading] = useState(combinedLoading)

useEffect(() => {
if (combinedLoading) {
setOverallDashboardLoading(true)
} else {
const timer = setTimeout(() => {
setOverallDashboardLoading(false)
}, 300)
return () => clearTimeout(timer)
}
}, [combinedLoading])

if (error) {
return (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
Expand All @@ -58,27 +86,28 @@ const Dashboard = () => {
<Card
title="Total Eligible Nodes"
bigNumber={formatNumber(totalEligibleNodes)}
isLoading={loading}
isLoading={overallDashboardLoading}
/>

<Card
title="Total Nodes"
bigNumber={formatNumber(totalNodes)}
isLoading={loading}
isLoading={overallDashboardLoading}
/>

{pathname === '/nodes' ? (
<Card
title="Rewards History"
chartType="line"
chartData={rewardsHistory}
isLoading={loading || !rewardsHistory}
isLoading={overallDashboardLoading}
dataLoading={overallDashboardLoading}
/>
) : (
<Card
title="Total Countries"
bigNumber={formatNumber(totalCountries)}
isLoading={loading}
isLoading={overallDashboardLoading || mapLoading}
dataLoading={mapLoading}
/>
)}
Expand All @@ -93,7 +122,7 @@ const Dashboard = () => {
<span className={styles.oceanText}>ROSE</span>
</div>
}
isLoading={loading}
isLoading={overallDashboardLoading}
/>
</div>
)
Expand Down
87 changes: 64 additions & 23 deletions src/components/PieChart/PieChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface PieChartCardProps {

const PieChartCard: React.FC<PieChartCardProps> = ({ data, title }) => {
const [activeIndex, setActiveIndex] = useState<number | undefined>(undefined)
const [lockedIndex, setLockedIndex] = useState<number | undefined>(undefined)
const [hoverText, setHoverText] = useState('Hover to see details')

const totalValue = useMemo(
Expand All @@ -22,14 +23,31 @@ const PieChartCard: React.FC<PieChartCardProps> = ({ data, title }) => {
)

const onPieEnter = (_: any, index: number) => {
setActiveIndex(index)
const percentage = ((data[index].value / totalValue) * 100).toFixed(2)
setHoverText(`${data[index].name}: ${percentage}%`)
if (lockedIndex === undefined) {
setActiveIndex(index)
const percentage = ((data[index].value / totalValue) * 100).toFixed(2)
setHoverText(`${data[index].name}: ${percentage}%`)
}
}

const onPieLeave = () => {
setActiveIndex(undefined)
setHoverText('Hover to see details')
if (lockedIndex === undefined) {
setActiveIndex(undefined)
setHoverText('Hover to see details')
}
}

const onPieClick = (_: any, index: number) => {
if (lockedIndex === index) {
setLockedIndex(undefined)
setActiveIndex(undefined)
setHoverText('Hover to see details')
} else {
setLockedIndex(index)
setActiveIndex(index)
const percentage = ((data[index].value / totalValue) * 100).toFixed(2)
setHoverText(`${data[index].name}: ${percentage}%`)
}
}

const renderActiveShape = (props: any) => {
Expand Down Expand Up @@ -60,10 +78,15 @@ const PieChartCard: React.FC<PieChartCardProps> = ({ data, title }) => {
}

const CustomTooltip = ({ active, payload }: any) => {
if (active && payload && payload.length) {
const { name, value, details } = payload[0].payload
const percentage = ((value / totalValue) * 100).toFixed(1)

const isActive = active || lockedIndex !== undefined
let item
if (lockedIndex !== undefined) {
item = data[lockedIndex]
} else if (payload && payload.length > 0) {
item = payload[0].payload
}
if (isActive && item) {
const percentage = ((item.value / totalValue) * 100).toFixed(1)
return (
<div
style={{
Expand All @@ -73,25 +96,26 @@ const PieChartCard: React.FC<PieChartCardProps> = ({ data, title }) => {
color: '#000'
}}
>
<p style={{ margin: '0 0 8px 0', fontWeight: 'bold' }}>{name}</p>
<p style={{ margin: '0 0 8px 0', fontWeight: 'bold' }}>{item.name}</p>
<p style={{ margin: '0 0 8px 0' }}>
Total: {value} nodes ({percentage}%)
Total: {item.value} nodes ({percentage}%)
</p>
{details && (
{item.details && (
<div
style={{
fontSize: '12px',
maxHeight: '150px',
height: '150px',
overflowY: 'auto',
borderTop: '1px solid #eee',
paddingTop: '8px'
paddingTop: '8px',
pointerEvents: 'auto'
}}
>
{Array.isArray(details) &&
details.map((detail: string, index: number) => (
<p key={index} style={{ margin: '2px 0' }}>
{detail}
</p>
{Array.isArray(item.details) &&
item.details.map((detail: string, index: number) => (
<div key={index}>
<p style={{ margin: '2px 0' }}>{detail}</p>
</div>
))}
</div>
)}
Expand All @@ -118,6 +142,7 @@ const PieChartCard: React.FC<PieChartCardProps> = ({ data, title }) => {
dataKey="value"
onMouseEnter={onPieEnter}
onMouseLeave={onPieLeave}
onClick={onPieClick}
className={styles.pieHover}
>
{data.map((entry, index) => (
Expand All @@ -126,16 +151,32 @@ const PieChartCard: React.FC<PieChartCardProps> = ({ data, title }) => {
fill={entry.color}
stroke="none"
style={{
transition: 'all 0.3s ease-in-out',
filter: activeIndex === index ? 'url(#glow)' : 'none'
transition: 'all 0.6s cubic-bezier(0.4, 0, 0.2, 1)',
filter:
activeIndex === index || lockedIndex === index
? 'url(#glow)'
: 'none',
transform:
activeIndex === index || lockedIndex === index
? 'scale(1.05)'
: 'scale(1)'
}}
/>
))}
</Pie>
<Tooltip content={<CustomTooltip />} />
<Tooltip
active={lockedIndex !== undefined ? true : undefined}
content={<CustomTooltip />}
position={{ y: 250 }}
wrapperStyle={{
transition: 'opacity 0.3s ease-in-out',
opacity: activeIndex !== undefined || lockedIndex !== undefined ? 1 : 0,
zIndex: 1000
}}
/>
</PieChart>
</ResponsiveContainer>
<p className={styles.tapToSee}>{hoverText}</p>
<div style={{ textAlign: 'center', marginTop: '8px' }}>{hoverText}</div>
</div>
)
}
Expand Down
13 changes: 10 additions & 3 deletions src/components/Table/CustomPagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ const NavButton = styled(Button)(({ theme }) => ({
padding: '6px',
'&.Mui-disabled': {
opacity: 0.5
},
'& .MuiTypography-root': {
fontFamily: "'Sharp Sans', sans-serif",
fontSize: '16px',
fontWeight: 400,
lineHeight: '24px'
}
}))

Expand Down Expand Up @@ -100,7 +106,8 @@ const StyledTextField = styled(TextField)(({ theme }) => ({
fontSize: '16px',
fontWeight: 400,
lineHeight: '24px',
color: '#000000'
color: '#000000',
minWidth: '42px'
},
'& .MuiInputBase-input::placeholder': {
color: '#A0AEC0',
Expand Down Expand Up @@ -219,7 +226,7 @@ const CustomPagination = React.memo(function CustomPagination({
</MenuItem>
))}
</StyledSelect>
<div className={styles.pageJumpContainer}>
<div>
<StyledTextField
size="small"
value={pageInput}
Expand Down Expand Up @@ -320,7 +327,7 @@ const CustomPagination = React.memo(function CustomPagination({
</MenuItem>
))}
</StyledSelect>
<div className={styles.pageJumpContainer}>
<div>
<StyledTextField
size="small"
value={pageInput}
Expand Down
10 changes: 8 additions & 2 deletions src/components/TopCountriesChart/TopCountriesChart .tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ const TopCountriesChart: React.FC = () => {
nodes: stat.totalNodes
}))

const maxNodes = Math.max(...topCountries.map((country) => country.nodes))

const tickInterval = maxNodes > 50000 ? 5000 : 1000
const roundedMax = Math.ceil(maxNodes / tickInterval) * tickInterval
const tickValues = Array.from({ length: 6 }, (_, i) => Math.round((roundedMax / 5) * i))

return (
<div className={styles.root}>
<h2 className={styles.title}>Top 5 countries by Ocean Nodes</h2>
Expand All @@ -30,8 +36,8 @@ const TopCountriesChart: React.FC = () => {
axisLine={false}
tickLine={false}
tick={<CustomXAxisTick />}
domain={[0, 10000]}
ticks={[0, 2000, 4000, 6000, 8000, 10000]}
domain={[0, roundedMax]}
ticks={tickValues}
/>
<YAxis
dataKey="country"
Expand Down
Loading

0 comments on commit 2744f67

Please sign in to comment.