diff --git a/.vscode/settings.json b/.vscode/settings.json index 6fc5e1f..765ead1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,8 +4,8 @@ }, "files.exclude": { "**/*.js.map": true, - "**/*.js": {"when": "$(basename).ts"}, - "**/*.map": {"when": "$(basename).map"}, + "**/*.js": { "when": "$(basename).ts" }, + "**/*.map": { "when": "$(basename).map" } }, "files.watcherExclude": { "**/.git/objects/**": true, @@ -18,7 +18,7 @@ "editor.codeActionsOnSave": { "source.organizeImports": "explicit" }, - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "typescript.preferences.importModuleSpecifier": "non-relative", "[typescriptreact]": { @@ -32,6 +32,7 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[css]": { + "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[json]": { @@ -39,7 +40,5 @@ }, "editor.tabSize": 2, "editor.detectIndentation": false, - "tailwindCSS.experimental.classRegex": [ - ["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] - ] + "tailwindCSS.experimental.classRegex": [["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]] } diff --git a/src/app/governance/[id]/page.tsx b/src/app/governance/[id]/page.tsx index 188a97d..3e15caa 100644 --- a/src/app/governance/[id]/page.tsx +++ b/src/app/governance/[id]/page.tsx @@ -1,8 +1,11 @@ 'use client'; import { useMemo } from 'react'; +import { FullWidthSpinner } from 'src/components/animation/Spinner'; import { BackLink } from 'src/components/buttons/BackLink'; +import { ExternalLink } from 'src/components/buttons/ExternalLink'; import { Section } from 'src/components/layout/Section'; +import { links } from 'src/config/links'; import { ProposalBadgeRow } from 'src/features/governance/ProposalCard'; import { MergedProposalData, @@ -11,6 +14,7 @@ import { import { useProposalContent } from 'src/features/governance/useProposalContent'; import { usePageInvariant } from 'src/utils/navigation'; import { trimToLength } from 'src/utils/strings'; +import styles from './styles.module.css'; const ID_PARAM_REGEX = /^(cgp-)?(\d+)$/; @@ -34,11 +38,14 @@ export default function Page({ params: { id } }: { params: { id: string } }) { }, [proposals, id]); usePageInvariant(!proposals || proposal, '/governance', 'Proposal not found'); - if (!proposal) return null; + + if (!proposal) { + return Loading proposals; + } return (
-
+
@@ -50,20 +57,49 @@ function ProposalContent({ data }: { data: MergedProposalData }) { const { proposal, metadata } = data; const title = trimToLength(metadata?.title || `Proposal #${proposal?.id}`, 80); - //TODO loading/err - const { content } = useProposalContent(metadata); + const { content, isLoading } = useProposalContent(metadata); return (
Browse proposals

{title}

-
+ {isLoading && !content && Loading proposal content} + {!isLoading && !content && ( +
+

No CGP content found for this proposal

+

+ Check the{' '} + + Celo Governance repository + {' '} + for more information +

+
+ )} + {content && ( +
+ )}
); } function ProposalChainData({ data: { proposal } }: { data: MergedProposalData }) { if (!proposal) return null; - return
TODO
; + return ( +
+
+

Vote

+
+
+

Results

+
+
+

Voters

+
+
+ ); } diff --git a/src/app/governance/[id]/styles.module.css b/src/app/governance/[id]/styles.module.css new file mode 100644 index 0000000..aea8371 --- /dev/null +++ b/src/app/governance/[id]/styles.module.css @@ -0,0 +1,59 @@ +.proposal h1, +.proposal h2, +.proposal h3, +.proposal h4 { + font-family: 'Garamond', serif; +} + +.proposal h1, +.proposal h2 { + font-size: 24px; +} + +.proposal h3 { + font-size: 20px; +} + +.proposal h4 { + font-size: 18px; +} + +.proposal a { + text-decoration: underline; + cursor: pointer; +} + +.proposal a:hover { + opacity: 0.75; +} + +.proposal a:active { + opacity: 0.65; +} + +.proposal ol, +.proposal ul { + padding-left: 1rem; +} + +.proposal ul { + list-style: disc; +} + +.proposal ol { + list-style: decimal; +} + +.proposal li { + padding-left: 1rem; + margin-top: 0.6rem; +} + +.proposal pre { + padding: 0.5rem; + background-color: rgba(0, 0, 0, 0.04); +} + +.proposal img { + display: none; +} diff --git a/src/app/governance/page.tsx b/src/app/governance/page.tsx index 6fd4930..d6799fa 100644 --- a/src/app/governance/page.tsx +++ b/src/app/governance/page.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import { useMemo, useState } from 'react'; import { Fade } from 'src/components/animation/Fade'; -import { SpinnerWithLabel } from 'src/components/animation/Spinner'; +import { FullWidthSpinner } from 'src/components/animation/Spinner'; import { TabHeaderFilters } from 'src/components/buttons/TabHeaderButton'; import { SearchField } from 'src/components/input/SearchField'; import { Section } from 'src/components/layout/Section'; @@ -109,9 +109,7 @@ function ProposalList() {
) : ( -
- Loading governance data -
+ Loading governance data )} ); diff --git a/src/app/page.tsx b/src/app/page.tsx index 8b2f783..7fe4f2e 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,7 @@ 'use client'; import { useMemo } from 'react'; import { Fade } from 'src/components/animation/Fade'; -import { SpinnerWithLabel } from 'src/components/animation/Spinner'; +import { FullWidthSpinner } from 'src/components/animation/Spinner'; import { SolidButton } from 'src/components/buttons/SolidButton'; import { Section } from 'src/components/layout/Section'; import { StatBox } from 'src/components/layout/StatBox'; @@ -84,11 +84,7 @@ function HeroSection({ totalVotes, groups }: { totalVotes?: bigint; groups?: Val function TableSection({ totalVotes, groups }: { totalVotes?: bigint; groups?: ValidatorGroup[] }) { if (!totalVotes || !groups) { - return ( -
- Loading validator data -
- ); + return Loading validator data; } return ( diff --git a/src/components/animation/Spinner.tsx b/src/components/animation/Spinner.tsx index 01ee12d..6dea142 100644 --- a/src/components/animation/Spinner.tsx +++ b/src/components/animation/Spinner.tsx @@ -19,6 +19,17 @@ export function SpinnerWithLabel({ ); } +export function FullWidthSpinner({ + children, + className, +}: PropsWithChildren<{ className?: string }>) { + return ( +
+ {children} +
+ ); +} + const sizeToClass = { xs: 'loading-xs', sm: 'loading-sm', diff --git a/src/config/links.ts b/src/config/links.ts index 4391269..b8e93fb 100644 --- a/src/config/links.ts +++ b/src/config/links.ts @@ -6,6 +6,7 @@ export const links = { twitter: 'https://twitter.com/CeloOrg', docs: 'https://docs.celo.org', forum: 'https://forum.celo.org', + governance: 'https://github.com/celo-org/governance', // RPCs forno: 'https://forno.celo.org', infura: 'https://celo-mainnet.infura.io/v3', diff --git a/src/features/staking/ActiveStakesTable.tsx b/src/features/staking/ActiveStakesTable.tsx index 1fc033a..7bf6f0b 100644 --- a/src/features/staking/ActiveStakesTable.tsx +++ b/src/features/staking/ActiveStakesTable.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import Image from 'next/image'; import { useMemo } from 'react'; -import { SpinnerWithLabel } from 'src/components/animation/Spinner'; +import { FullWidthSpinner } from 'src/components/animation/Spinner'; import { SolidButton } from 'src/components/buttons/SolidButton'; import { PLACEHOLDER_BAR_CHART_ITEM, StackedBarChart } from 'src/components/charts/StackedBarChart'; import { sortAndCombineChartData } from 'src/components/charts/chartData'; @@ -63,11 +63,7 @@ export function ActiveStakesTable({ }, [groupToStake, addressToGroup]); if (!groupToStake || !addressToGroup || !groupToIsActivatable) { - return ( -
- Loading staking data -
- ); + return Loading staking data; } if (!objLength(groupToStake)) { diff --git a/src/features/staking/rewards/RewardsTable.tsx b/src/features/staking/rewards/RewardsTable.tsx index fe0edf6..0768077 100644 --- a/src/features/staking/rewards/RewardsTable.tsx +++ b/src/features/staking/rewards/RewardsTable.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { SpinnerWithLabel } from 'src/components/animation/Spinner'; +import { FullWidthSpinner } from 'src/components/animation/Spinner'; import { SolidButton } from 'src/components/buttons/SolidButton'; import { PLACEHOLDER_BAR_CHART_ITEM, StackedBarChart } from 'src/components/charts/StackedBarChart'; import { sortAndCombineChartData } from 'src/components/charts/chartData'; @@ -48,11 +48,7 @@ export function RewardsTable({ }, [groupToReward, addressToGroup]); if (!groupToReward || !addressToGroup) { - return ( -
- Loading staking data -
- ); + return Loading staking data; } if (!objLength(groupToReward)) {