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/dashboard v2 enhancements #67

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
131 changes: 129 additions & 2 deletions src/components/Card/Card.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,30 @@
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
height: 100%;
min-height: 238px;
aspect-ratio: 1 / 1;
width: 100%;
position: relative;
animation: fadeIn 0.5s ease-in;
}

@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

.cardContent {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
position: absolute;
inset: 20px;
}

.cardTitle {
Expand Down Expand Up @@ -65,3 +80,115 @@
line-height: 24px;
}
}

.cardLoading {
position: relative;
overflow: hidden;
}

.cardLoading .cardTitle,
.cardLoading .bigNumber,
.cardLoading .subText {
position: relative;
background: #f6f7f8;
border-radius: 4px;
overflow: hidden;
}

.cardLoading .cardTitle {
height: 24px;
width: 70%;
}

.cardLoading .bigNumber {
height: 60px;
width: 50%;
margin: 100px auto;
}

.cardLoading .subText {
height: 20px;
width: 40%;
margin-left: auto;
margin-top: auto;
margin-bottom: 0;
}

.cardLoading .cardTitle::after,
.cardLoading .bigNumber::after,
.cardLoading .subText::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 100%;
background: linear-gradient(
90deg,
transparent 0%,
rgba(207, 31, 177, 0.2) 50%,
transparent 100%
);
animation: shimmer 2s infinite linear;
transform: translateX(-100%);
}

.cardLoading .bigNumber::after {
animation-delay: 0.1s;
}

.cardLoading .subText::after {
animation-delay: 0.2s;
}

@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(50%);
}
}

.skeletonText {
background: #f0f0f0;
border-radius: 4px;
height: 24px;
width: 80%;
margin-bottom: 15px;
}

.skeletonNumber {
background: #f0f0f0;
border-radius: 4px;
height: 60px;
width: 60%;
margin: 20px auto;
}

.dataLoading {
position: relative;
background: #f6f7f8;
border-radius: 4px;
overflow: hidden;
height: 60px;
width: 50%;
margin: 95px auto;
}

.dataLoading::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 100%;
background: linear-gradient(
90deg,
transparent 0%,
rgba(207, 31, 177, 0.2) 50%,
transparent 100%
);
animation: shimmer 2s infinite linear;
transform: translateX(-100%);
}
133 changes: 82 additions & 51 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@ interface CardProps {
bigNumber?: string | number
subText?: string
additionalInfo?: React.ReactNode
}

const getStrokeColor = ({ channel }: { channel: string }) => {
return channel === 'foreground' ? 'url(#gradient)' : '#E0E0E0'
isLoading?: boolean
dataLoading?: boolean
}

const CustomBar = (props: any) => {
Expand Down Expand Up @@ -74,58 +72,91 @@ const Card: React.FC<CardProps> = ({
chartData,
bigNumber,
subText,
additionalInfo
additionalInfo,
isLoading = false,
dataLoading = false
}) => {
const formatNumber = (num: string | number) => {
if (typeof num === 'string') return num

if (num >= 1000 && num < 1000000) {
return `${(num / 1000).toFixed(1)}K`
}
if (num >= 1000000) {
return `${(num / 1000000).toFixed(2)}M`
}
return new Intl.NumberFormat('en-US').format(num)
}

return (
<div className={styles.card}>
<h3 className={styles.cardTitle}>{title}</h3>
<div className={`${styles.card} ${isLoading ? styles.cardLoading : ''}`}>
<div className={styles.cardContent}>
{chartType === 'bar' && chartData && (
<ResponsiveContainer width="100%" height={100}>
<BarChart data={chartData} barSize={15}>
<defs>
<linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#CF1FB1" />
<stop offset="100%" stopColor="#DA4A8C" />
</linearGradient>
</defs>
<XAxis dataKey="date" hide />
<Bar
dataKey="background.value"
shape={(props: any) => (
<CustomBar
{...props}
foregroundValue={props.payload.foreground.value}
backgroundValue={props.payload.background.value}
/>
{isLoading ? (
<>
<div className={styles.cardTitle} aria-hidden="true" />
<div className={styles.bigNumber} aria-hidden="true" />
{chartType && <div className={styles.chartSkeleton} aria-hidden="true" />}
{subText && <div className={styles.subText} aria-hidden="true" />}
</>
) : (
<>
<h3 className={styles.cardTitle}>{title}</h3>
{dataLoading ? (
<div className={styles.dataLoading} aria-hidden="true" />
) : (
<>
{chartType === 'bar' && chartData && (
<ResponsiveContainer width="100%" height={100}>
<BarChart data={chartData} barSize={15}>
<defs>
<linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#CF1FB1" />
<stop offset="100%" stopColor="#DA4A8C" />
</linearGradient>
</defs>
<XAxis dataKey="date" hide />
<Bar
dataKey="background.value"
shape={(props: any) => (
<CustomBar
{...props}
foregroundValue={props.payload.foreground.value}
backgroundValue={props.payload.background.value}
/>
)}
/>
<Tooltip />
</BarChart>
</ResponsiveContainer>
)}
/>
<Tooltip />
</BarChart>
</ResponsiveContainer>
)}
{chartType === 'line' && chartData && (
<ResponsiveContainer width="100%" height={100}>
<LineChart data={chartData}>
<defs>
<linearGradient id="lineGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="20%" stopColor="#CF1FB1" stopOpacity={1} />
</linearGradient>
</defs>
<Line
type="monotone"
dataKey="value"
stroke="url(#lineGradient)"
strokeWidth={2}
dot={false}
/>
<Tooltip />
</LineChart>
</ResponsiveContainer>
{chartType === 'line' && chartData && (
<ResponsiveContainer width="100%" height={100}>
<LineChart data={chartData}>
<defs>
<linearGradient id="lineGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="20%" stopColor="#CF1FB1" stopOpacity={1} />
</linearGradient>
</defs>
<Line
type="monotone"
dataKey="value"
stroke="url(#lineGradient)"
strokeWidth={2}
dot={false}
/>
<Tooltip />
</LineChart>
</ResponsiveContainer>
)}
{bigNumber && (
<div className={styles.bigNumber}>{formatNumber(bigNumber)}</div>
)}
{subText && <p className={styles.subText}>{subText}</p>}
{additionalInfo}
</>
)}
</>
)}
{bigNumber && <div className={styles.bigNumber}>{bigNumber}</div>}
{subText && <p className={styles.subText}>{subText}</p>}
{additionalInfo}
</div>
</div>
)
Expand Down
32 changes: 19 additions & 13 deletions src/components/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,10 @@ const formatRewardsNumber = (num: number | string | undefined): string => {
const Dashboard = () => {
const { loading, error, totalNodes, totalEligibleNodes, totalRewards, rewardsHistory } =
useDataContext()
const { totalCountries } = useMapContext()
const { totalCountries, loading: mapLoading } = useMapContext()
const pathname = usePathname()

if (pathname === '/nodes') return null

if (loading) {
return (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
<CircularProgress sx={{ color: '#e000cf' }} />
</Box>
)
}
if (pathname === '/nodes') return null

if (error) {
return (
Expand All @@ -65,9 +57,22 @@ const Dashboard = () => {

return (
<div className={styles.dashboard}>
<Card title="Total Eligible Nodes" bigNumber={formatNumber(totalEligibleNodes)} />
<Card title="Total Countries" bigNumber={formatNumber(totalCountries)} />
<Card title="Total Nodes" bigNumber={formatNumber(totalNodes)} />
<Card
title="Total Eligible Nodes"
bigNumber={formatNumber(totalEligibleNodes)}
isLoading={loading}
/>
<Card
title="Total Countries"
bigNumber={formatNumber(totalCountries)}
isLoading={loading}
dataLoading={mapLoading}
/>
<Card
title="Total Nodes"
bigNumber={formatNumber(totalNodes)}
isLoading={loading}
/>
<Card
title="Total Rewards"
additionalInfo={
Expand All @@ -78,6 +83,7 @@ const Dashboard = () => {
<span className={styles.oceanText}>ROSE</span>
</div>
}
isLoading={loading}
/>
</div>
)
Expand Down
13 changes: 11 additions & 2 deletions src/components/Map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import L, { LatLngExpression } from 'leaflet'
import 'leaflet/dist/leaflet.css'
import styles from './style.module.css'
import { useMapContext } from '../../context/MapContext'
import { CircularProgress, Alert, Box, LinearProgress } from '@mui/material'
import { Alert, Box, LinearProgress, Skeleton } from '@mui/material'
import { LocationNode } from '../../shared/types/locationNodeType'

export default function Map() {
Expand All @@ -20,7 +20,15 @@ export default function Map() {
if (loading || !data) {
return (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
<CircularProgress sx={{ color: '#e000cf' }} />
<Skeleton
variant="rectangular"
width="100%"
height={500}
sx={{
borderRadius: '20px',
bgcolor: 'rgba(207, 31, 177, 0.1)' // Matches our theme
}}
/>
</Box>
)
}
Expand Down Expand Up @@ -91,6 +99,7 @@ export default function Map() {
<MapContainer
center={center}
zoom={2}
className={styles.mapContainer}
style={{ height: '500px', width: '100%', borderRadius: '20px' }}
>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
Expand Down
Loading
Loading