Skip to content

Commit 83c5cd4

Browse files
authored
Merge pull request #2346 from oasisprotocol/mz/homepage-ecosystem
Create ecosystem components for homepage
2 parents 66b8345 + 136b2ee commit 83c5cd4

File tree

14 files changed

+718
-3
lines changed

14 files changed

+718
-3
lines changed

.changelog/2346.trivial.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Create ecosystem components for homepage

src/app/pages/ConsensusDashboardPage/RuntimePreview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ const RuntimePreview: FC<RuntimePreviewProps> = ({
131131
<dd className="flex justify-end py-1">
132132
<div>
133133
{runtimeStatus === 'stable' && <Badge variant="success">{t('common.online')}</Badge>}
134-
{runtimeStatus === 'outdated' && <Badge variant="error">{t('paratimes.outdated')}</Badge>}
134+
{runtimeStatus === 'outdated' && <Badge variant="error">{t('common.outdated')}</Badge>}
135135
</div>
136136
</dd>
137137
<dt className="flex items-start py-1">{t('paratimes.blockNumber')}</dt>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { FC } from 'react'
2+
import { useTranslation } from 'react-i18next'
3+
import { useConsensusFreshness } from 'app/components/OfflineBanner/hook'
4+
import { useGetConsensusValidators, useGetStatus } from 'oasis-nexus/api'
5+
import { EcosystemCard } from './EcosystemCard'
6+
import consensusBg from './images/consensus-bg.svg'
7+
8+
export const ConsensusCard: FC = () => {
9+
const { t } = useTranslation()
10+
const consensusStatusQuery = useGetStatus('mainnet')
11+
const { outOfDate: consensusOutOfDate } = useConsensusFreshness('mainnet')
12+
const validatorsQuery = useGetConsensusValidators('mainnet', { limit: 1000 })
13+
const activeValidatorsCount = validatorsQuery.data?.data.validators.filter(v => v.in_validator_set).length
14+
15+
return (
16+
<EcosystemCard
17+
isLoading={consensusStatusQuery.isLoading || validatorsQuery.isLoading}
18+
description={t('home.ecosystem.consensus')}
19+
title={t('common.consensus')}
20+
background={consensusBg}
21+
layer="consensus"
22+
outOfDate={consensusOutOfDate}
23+
latestBlock={consensusStatusQuery?.data?.data?.latest_block}
24+
activeNodes={activeValidatorsCount}
25+
/>
26+
)
27+
}
28+
29+
export const ConsensusFallbackCard: FC = () => {
30+
const { t } = useTranslation()
31+
32+
return (
33+
<EcosystemCard
34+
description={t('home.ecosystem.consensus')}
35+
title={t('common.consensus')}
36+
background={consensusBg}
37+
layer="consensus"
38+
/>
39+
)
40+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { FC } from 'react'
2+
import { useTranslation } from 'react-i18next'
3+
import { Typography } from '@oasisprotocol/ui-library/src/components/typography'
4+
import { ErrorBoundary } from '../../components/ErrorBoundary'
5+
import { ConsensusCard, ConsensusFallbackCard } from './ConsensusCard'
6+
import { SapphireCard, SapphireFallbackCard } from './SapphireCard'
7+
import { EmeraldCard, EmeraldFallbackCard } from './EmeraldCard'
8+
import { PontusXFallbackCard, PontusXCard } from './PontusXCard'
9+
10+
export const Ecosystem: FC = () => {
11+
const { t } = useTranslation()
12+
13+
return (
14+
<div className="flex flex-col gap-6">
15+
<Typography variant="h3"> {t('home.ecosystem.title')}</Typography>
16+
17+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
18+
<ErrorBoundary light fallbackContent={<SapphireFallbackCard />}>
19+
<SapphireCard />
20+
</ErrorBoundary>
21+
<ErrorBoundary light fallbackContent={<ConsensusFallbackCard />}>
22+
<ConsensusCard />
23+
</ErrorBoundary>
24+
<ErrorBoundary light fallbackContent={<PontusXFallbackCard />}>
25+
<PontusXCard />
26+
</ErrorBoundary>
27+
<ErrorBoundary light fallbackContent={<EmeraldFallbackCard />}>
28+
<EmeraldCard />
29+
</ErrorBoundary>
30+
</div>
31+
</div>
32+
)
33+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { FC, ReactNode } from 'react'
2+
import { Link } from 'react-router-dom'
3+
import { useTranslation } from 'react-i18next'
4+
import { ArrowRight } from 'lucide-react'
5+
import {
6+
Card,
7+
CardContent,
8+
CardFooter,
9+
CardHeader,
10+
CardTitle,
11+
} from '@oasisprotocol/ui-library/src/components/cards'
12+
import { Button } from '@oasisprotocol/ui-library/src/components/ui/button'
13+
import { Typography } from '@oasisprotocol/ui-library/src/components/typography'
14+
import { Badge } from '@oasisprotocol/ui-library/src/components/badge'
15+
import { Skeleton } from '@oasisprotocol/ui-library/src/components/ui/skeleton'
16+
import { RouteUtils } from '../../utils/route-utils'
17+
18+
type EcosystemCardProps = {
19+
activeNodes?: number
20+
background: string
21+
footer?: ReactNode
22+
isLoading?: boolean
23+
latestBlock?: number
24+
layer: 'consensus' | 'sapphire' | 'emerald' | 'pontusxtest'
25+
outOfDate?: boolean
26+
title: ReactNode
27+
description: ReactNode
28+
legacy?: boolean
29+
}
30+
31+
export const EcosystemCard: FC<EcosystemCardProps> = ({
32+
activeNodes,
33+
background,
34+
footer,
35+
isLoading,
36+
latestBlock,
37+
layer,
38+
outOfDate,
39+
title,
40+
description,
41+
legacy,
42+
}) => {
43+
const { t } = useTranslation()
44+
45+
return (
46+
<Card
47+
className="bg-right-bottom bg-no-repeat"
48+
style={{
49+
backgroundImage: `url(${background})`,
50+
}}
51+
>
52+
<CardHeader>
53+
<CardTitle className="mb-2 text-lg items-center gap-2">
54+
{title}
55+
56+
{legacy && (
57+
<Badge variant="warning" className="h-5">
58+
{t('common.legacy')}
59+
</Badge>
60+
)}
61+
62+
{!outOfDate && !legacy && !isLoading && (
63+
<Badge variant="success" className="h-5">
64+
{t('common.online')}
65+
</Badge>
66+
)}
67+
{outOfDate && !legacy && !isLoading && (
68+
<Badge variant="error" className="h-5">
69+
{t('common.outdated')}
70+
</Badge>
71+
)}
72+
</CardTitle>
73+
<Typography textColor="muted" variant="p" className="text-pretty">
74+
{description}
75+
</Typography>
76+
</CardHeader>
77+
<CardContent className="flex-1 flex items-end">
78+
{isLoading && <Skeleton className="h-14 w-full" />}
79+
{!isLoading && (
80+
<div className="w-full grid grid-cols-1 md:grid-cols-2 gap-4 font-medium">
81+
<div>{t('home.blockNumber')}</div>
82+
<div className="text-right text-primary">
83+
{latestBlock !== undefined && latestBlock !== null ? latestBlock.toLocaleString() : '-'}
84+
</div>
85+
<div>{t('home.activeNodes')}</div>
86+
<div className="text-right text-primary">
87+
{activeNodes !== undefined && activeNodes !== null ? activeNodes.toLocaleString() : '-'}
88+
</div>
89+
</div>
90+
)}
91+
</CardContent>
92+
<CardFooter>
93+
{footer || (
94+
<>
95+
<Button variant="link" size="lg" className="flex-1">
96+
<Link to={RouteUtils.getDashboardRoute({ network: 'testnet', layer })}>
97+
{t('common.testnet')}
98+
</Link>
99+
</Button>
100+
<Button variant="secondary" size="lg" className="flex-1">
101+
<Link to={RouteUtils.getDashboardRoute({ network: 'mainnet', layer })}>
102+
{t('common.mainnet')}
103+
</Link>
104+
<ArrowRight />
105+
</Button>
106+
</>
107+
)}
108+
</CardFooter>
109+
</Card>
110+
)
111+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { FC } from 'react'
2+
import { useTranslation } from 'react-i18next'
3+
import { useGetRuntimeStatus } from '../../../oasis-nexus/api'
4+
import { EcosystemCard } from './EcosystemCard'
5+
import emeraldBg from './images/emerald-bg.svg'
6+
7+
export const EmeraldCard: FC = () => {
8+
const { t } = useTranslation()
9+
const emeraldStatusQuery = useGetRuntimeStatus('mainnet', 'emerald')
10+
11+
return (
12+
<EcosystemCard
13+
isLoading={emeraldStatusQuery.isLoading}
14+
description={t('home.ecosystem.emerald')}
15+
title={t('common.emerald')}
16+
background={emeraldBg}
17+
layer="emerald"
18+
legacy
19+
latestBlock={emeraldStatusQuery?.data?.data?.latest_block}
20+
activeNodes={emeraldStatusQuery?.data?.data?.active_nodes}
21+
/>
22+
)
23+
}
24+
25+
export const EmeraldFallbackCard: FC = () => {
26+
const { t } = useTranslation()
27+
28+
return (
29+
<EcosystemCard
30+
description={t('home.ecosystem.emerald')}
31+
title={t('common.emerald')}
32+
background={emeraldBg}
33+
layer="emerald"
34+
legacy
35+
/>
36+
)
37+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { FC } from 'react'
2+
import { Link as RouterLink } from 'react-router-dom'
3+
import { useTranslation } from 'react-i18next'
4+
import { ArrowRight } from 'lucide-react'
5+
import { useGetRuntimeStatus } from '../../../oasis-nexus/api'
6+
import { Button } from '@oasisprotocol/ui-library/src/components/ui/button'
7+
import { Tooltip } from '@oasisprotocol/ui-library/src/components/tooltip'
8+
import { Link } from '@oasisprotocol/ui-library/src/components/link'
9+
import { RouteUtils } from '../../utils/route-utils'
10+
import { useRuntimeFreshness } from '../../components/OfflineBanner/hook'
11+
import { pontusx } from '../../utils/externalLinks'
12+
import { EcosystemCard } from './EcosystemCard'
13+
import pontusxBg from './images/pontusx-bg.svg'
14+
15+
export const PontusXCard: FC = () => {
16+
const { t } = useTranslation()
17+
const pontusxStatusQuery = useGetRuntimeStatus('testnet', 'pontusxtest')
18+
const { outOfDate: pontusxOutOfDate } = useRuntimeFreshness({
19+
network: 'testnet',
20+
layer: 'pontusxtest',
21+
})
22+
23+
return (
24+
<EcosystemCard
25+
isLoading={pontusxStatusQuery.isLoading}
26+
description={
27+
<>
28+
{t('home.ecosystem.pontusx')} <span className="italic">{t('home.ecosystem.comingSoon')}</span>
29+
</>
30+
}
31+
title={
32+
<span>
33+
{t('common.pontusx')}{' '}
34+
<span className="text-sm text-muted-foreground font-normal">
35+
{t('home.ecosystem.by')}
36+
<Tooltip
37+
title={
38+
<>
39+
{t('home.ecosystem.deltaDAODescription')}{' '}
40+
<Link
41+
textColor="inherit"
42+
variant="underline"
43+
href={pontusx.homepage}
44+
rel="noopener noreferrer"
45+
target="_blank"
46+
>
47+
{t('home.ecosystem.visitSite')}
48+
</Link>
49+
</>
50+
}
51+
>
52+
<span className="italic underline">{t('home.ecosystem.deltaDAO')}</span>
53+
</Tooltip>
54+
</span>
55+
</span>
56+
}
57+
background={pontusxBg}
58+
layer="pontusxtest"
59+
outOfDate={pontusxOutOfDate}
60+
latestBlock={pontusxStatusQuery?.data?.data?.latest_block}
61+
activeNodes={pontusxStatusQuery?.data?.data?.active_nodes}
62+
footer={
63+
<>
64+
<Button variant="secondary" size="lg" className="flex-1">
65+
<RouterLink to={RouteUtils.getDashboardRoute({ network: 'testnet', layer: 'pontusxtest' })}>
66+
{t('common.testnet')}
67+
</RouterLink>
68+
<ArrowRight />
69+
</Button>
70+
<Button variant="link" size="lg" className="flex-1">
71+
<RouterLink to={RouteUtils.getDashboardRoute({ network: 'testnet', layer: 'pontusxdev' })}>
72+
{t('common.devnet')}
73+
</RouterLink>
74+
</Button>
75+
</>
76+
}
77+
/>
78+
)
79+
}
80+
81+
export const PontusXFallbackCard: FC = () => {
82+
const { t } = useTranslation()
83+
84+
return (
85+
<EcosystemCard
86+
description={t('home.ecosystem.pontusx')}
87+
title={t('common.pontusx')}
88+
background={pontusxBg}
89+
layer="pontusxtest"
90+
/>
91+
)
92+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { FC } from 'react'
2+
import { useTranslation } from 'react-i18next'
3+
import { useGetRuntimeStatus } from '../../../oasis-nexus/api'
4+
import { useRuntimeFreshness } from '../../components/OfflineBanner/hook'
5+
import { EcosystemCard } from './EcosystemCard'
6+
import sapphireBg from './images/sapphire-bg.svg'
7+
8+
export const SapphireCard: FC = () => {
9+
const { t } = useTranslation()
10+
const sapphireStatusQuery = useGetRuntimeStatus('mainnet', 'sapphire')
11+
const { outOfDate: sapphireOutOfDate } = useRuntimeFreshness({
12+
network: 'mainnet',
13+
layer: 'sapphire',
14+
})
15+
16+
return (
17+
<EcosystemCard
18+
isLoading={sapphireStatusQuery.isLoading}
19+
description={t('home.ecosystem.sapphire')}
20+
title={t('common.sapphire')}
21+
background={sapphireBg}
22+
layer="sapphire"
23+
outOfDate={sapphireOutOfDate}
24+
latestBlock={sapphireStatusQuery?.data?.data?.latest_block}
25+
activeNodes={sapphireStatusQuery?.data?.data?.active_nodes}
26+
/>
27+
)
28+
}
29+
30+
export const SapphireFallbackCard: FC = () => {
31+
const { t } = useTranslation()
32+
33+
return (
34+
<EcosystemCard
35+
description={t('home.ecosystem.sapphire')}
36+
title={t('common.sapphire')}
37+
background={sapphireBg}
38+
layer="sapphire"
39+
/>
40+
)
41+
}

0 commit comments

Comments
 (0)