diff --git a/public/images/Core/safe-core-protocol.png b/public/images/Core/safe-core-protocol.png deleted file mode 100644 index 0edde332c..000000000 Binary files a/public/images/Core/safe-core-protocol.png and /dev/null differ diff --git a/public/images/Governance/Parallaxes/DaoStats/background.svg b/public/images/Governance/Parallaxes/DaoStats/background.svg new file mode 100644 index 000000000..9083b60de --- /dev/null +++ b/public/images/Governance/Parallaxes/DaoStats/background.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/Governance/Parallaxes/DaoStats/delegates.png b/public/images/Governance/Parallaxes/DaoStats/delegates.png new file mode 100644 index 000000000..f170ce71f Binary files /dev/null and b/public/images/Governance/Parallaxes/DaoStats/delegates.png differ diff --git a/public/images/Governance/Parallaxes/DaoStats/delegators.png b/public/images/Governance/Parallaxes/DaoStats/delegators.png new file mode 100644 index 000000000..91fca6e85 Binary files /dev/null and b/public/images/Governance/Parallaxes/DaoStats/delegators.png differ diff --git a/public/images/Governance/Parallaxes/DaoStats/proposals.png b/public/images/Governance/Parallaxes/DaoStats/proposals.png new file mode 100644 index 000000000..46ac769b0 Binary files /dev/null and b/public/images/Governance/Parallaxes/DaoStats/proposals.png differ diff --git a/public/images/Governance/Parallaxes/GovernanceProcess/0.png b/public/images/Governance/Parallaxes/GovernanceProcess/0.png new file mode 100644 index 000000000..e9fbbd6fd Binary files /dev/null and b/public/images/Governance/Parallaxes/GovernanceProcess/0.png differ diff --git a/public/images/Governance/Parallaxes/GovernanceProcess/1.png b/public/images/Governance/Parallaxes/GovernanceProcess/1.png new file mode 100644 index 000000000..35bfcc063 Binary files /dev/null and b/public/images/Governance/Parallaxes/GovernanceProcess/1.png differ diff --git a/public/images/Governance/Parallaxes/GovernanceProcess/2.png b/public/images/Governance/Parallaxes/GovernanceProcess/2.png new file mode 100644 index 000000000..3bf36f7f9 Binary files /dev/null and b/public/images/Governance/Parallaxes/GovernanceProcess/2.png differ diff --git a/public/images/Governance/Parallaxes/GovernanceProcess/background.svg b/public/images/Governance/Parallaxes/GovernanceProcess/background.svg new file mode 100644 index 000000000..6ccb4c1e0 --- /dev/null +++ b/public/images/Governance/Parallaxes/GovernanceProcess/background.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/Governance/Parallaxes/GovernanceProcess/leftLine.svg b/public/images/Governance/Parallaxes/GovernanceProcess/leftLine.svg new file mode 100644 index 000000000..6ed40b81f --- /dev/null +++ b/public/images/Governance/Parallaxes/GovernanceProcess/leftLine.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/Governance/Parallaxes/GovernanceProcess/rightLine.svg b/public/images/Governance/Parallaxes/GovernanceProcess/rightLine.svg new file mode 100644 index 000000000..9b5f611cb --- /dev/null +++ b/public/images/Governance/Parallaxes/GovernanceProcess/rightLine.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/Governance/ecosystem-avatars.png b/public/images/Governance/ecosystem-avatars.png new file mode 100644 index 000000000..b57c14357 Binary files /dev/null and b/public/images/Governance/ecosystem-avatars.png differ diff --git a/public/images/Governance/particles-globe-spin.gif b/public/images/Governance/particles-globe-spin.gif new file mode 100644 index 000000000..21b057373 Binary files /dev/null and b/public/images/Governance/particles-globe-spin.gif differ diff --git a/public/images/Wallet/batch-transactions.png b/public/images/Wallet/batch-transactions.png deleted file mode 100644 index 6e5e3ed55..000000000 Binary files a/public/images/Wallet/batch-transactions.png and /dev/null differ diff --git a/public/images/Wallet/co-manage.png b/public/images/Wallet/co-manage.png deleted file mode 100644 index 1e3944f2f..000000000 Binary files a/public/images/Wallet/co-manage.png and /dev/null differ diff --git a/public/images/Wallet/ownership.png b/public/images/Wallet/ownership.png deleted file mode 100644 index 31240f666..000000000 Binary files a/public/images/Wallet/ownership.png and /dev/null differ diff --git a/public/images/Wallet/pocket-multisig.png b/public/images/Wallet/pocket-multisig.png deleted file mode 100644 index 5545ef87d..000000000 Binary files a/public/images/Wallet/pocket-multisig.png and /dev/null differ diff --git a/public/images/Wallet/safe-apps-store.png b/public/images/Wallet/safe-apps-store.png deleted file mode 100644 index 89c6b87aa..000000000 Binary files a/public/images/Wallet/safe-apps-store.png and /dev/null differ diff --git a/public/images/Wallet/signer-types.png b/public/images/Wallet/signer-types.png deleted file mode 100644 index 159c7c805..000000000 Binary files a/public/images/Wallet/signer-types.png and /dev/null differ diff --git a/public/images/Wallet/simulate-transactions.png b/public/images/Wallet/simulate-transactions.png deleted file mode 100644 index 193af3bce..000000000 Binary files a/public/images/Wallet/simulate-transactions.png and /dev/null differ diff --git a/public/images/Wallet/wallet-QR.png b/public/images/Wallet/wallet-QR.png deleted file mode 100644 index db0edd9b4..000000000 Binary files a/public/images/Wallet/wallet-QR.png and /dev/null differ diff --git a/public/images/banner-lines.svg b/public/images/banner-lines.svg new file mode 100644 index 000000000..d57d8ad60 --- /dev/null +++ b/public/images/banner-lines.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/building-gradient.svg b/public/images/building-gradient.svg new file mode 100644 index 000000000..b9befb1b9 --- /dev/null +++ b/public/images/building-gradient.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/contract.png b/public/images/contract.png deleted file mode 100644 index 362159fcd..000000000 Binary files a/public/images/contract.png and /dev/null differ diff --git a/public/images/discord-icon.svg b/public/images/discord-icon.svg index 9c11e2249..d78504a81 100644 --- a/public/images/discord-icon.svg +++ b/public/images/discord-icon.svg @@ -1,3 +1,3 @@ - - + + diff --git a/public/images/ecosystem-bg.png b/public/images/ecosystem-bg.png deleted file mode 100644 index 1f4071150..000000000 Binary files a/public/images/ecosystem-bg.png and /dev/null differ diff --git a/public/images/external-link-gradient.svg b/public/images/external-link-gradient.svg new file mode 100644 index 000000000..0ab7c1809 --- /dev/null +++ b/public/images/external-link-gradient.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/github-icon.svg b/public/images/github-icon.svg index 4746b1190..b01d2ca4e 100644 --- a/public/images/github-icon.svg +++ b/public/images/github-icon.svg @@ -1,17 +1,10 @@ - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/public/images/hub-locations.png b/public/images/hub-locations.png deleted file mode 100644 index 7cc6bf621..000000000 Binary files a/public/images/hub-locations.png and /dev/null differ diff --git a/public/images/mirror-icon.svg b/public/images/mirror-icon.svg index 086f0d244..be495ed23 100644 --- a/public/images/mirror-icon.svg +++ b/public/images/mirror-icon.svg @@ -1,11 +1,3 @@ - - - - - - - - - - + + diff --git a/public/images/protocol.png b/public/images/protocol.png deleted file mode 100644 index 7333b7762..000000000 Binary files a/public/images/protocol.png and /dev/null differ diff --git a/public/images/shield-check-gradient.svg b/public/images/shield-check-gradient.svg new file mode 100644 index 000000000..148c08e16 --- /dev/null +++ b/public/images/shield-check-gradient.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/teach-gradient.svg b/public/images/teach-gradient.svg new file mode 100644 index 000000000..1bcf4bc64 --- /dev/null +++ b/public/images/teach-gradient.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/tokens-gradient.svg b/public/images/tokens-gradient.svg new file mode 100644 index 000000000..989ebca55 --- /dev/null +++ b/public/images/tokens-gradient.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/x-icon.svg b/public/images/x-icon.svg index 6adcf3562..b5700350c 100644 --- a/public/images/x-icon.svg +++ b/public/images/x-icon.svg @@ -1,3 +1,3 @@ - - + + diff --git a/public/images/youtube-icon.svg b/public/images/youtube-icon.svg index 862f505af..3a1dc4bf2 100644 --- a/public/images/youtube-icon.svg +++ b/public/images/youtube-icon.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/components/Careers/ParallaxLocations/index.tsx b/src/components/Careers/ParallaxLocations/index.tsx index a5e9f3481..1ea01b4e0 100644 --- a/src/components/Careers/ParallaxLocations/index.tsx +++ b/src/components/Careers/ParallaxLocations/index.tsx @@ -1,11 +1,11 @@ import ParallaxLocationsElement from '@/components/Careers/ParallaxLocations/ParallaxLocationsElement' -import ParallaxText, { type ParallaxTextProps } from '@/components/Careers/ParallaxText' +import ParallaxTextCentered, { type ParallaxTextCenteredProps } from '@/components/Careers/ParallaxText' -const ParallaxLocations = (props: ParallaxTextProps) => { +const ParallaxLocations = (props: ParallaxTextCenteredProps) => { return ( - + - + ) } diff --git a/src/components/Careers/ParallaxText/index.tsx b/src/components/Careers/ParallaxText/index.tsx index 3c49ea837..eb02c0d4d 100644 --- a/src/components/Careers/ParallaxText/index.tsx +++ b/src/components/Careers/ParallaxText/index.tsx @@ -4,13 +4,13 @@ import layoutCss from '@/components/common/styles.module.css' import css from './styles.module.css' import clsx from 'clsx' -export type ParallaxTextProps = { +export type ParallaxTextCenteredProps = { title: string text: string children: ReactNode } -const ParallaxText = ({ title, text, children }: ParallaxTextProps): ReactElement => { +const ParallaxTextCentered = ({ title, text, children }: ParallaxTextCenteredProps): ReactElement => { return ( { return ( - +
- - - {caption} - - } - className={css.chip} - variant="outlined" - /> - - {title} - - - + + {caption} + + } + className={css.chip} + variant="outlined" + /> + + {title} + +
) diff --git a/src/components/Core/BannerCta/styles.module.css b/src/components/Core/BannerCta/styles.module.css index 924a7fe26..e8392ee1e 100644 --- a/src/components/Core/BannerCta/styles.module.css +++ b/src/components/Core/BannerCta/styles.module.css @@ -1,7 +1,3 @@ -.wrapper { - margin-bottom: 80px; -} - .container { text-align: center; background-color: var(--mui-palette-border-light); diff --git a/src/components/Core/LinkedCardGrid/index.tsx b/src/components/Core/LinkedCardGrid/index.tsx index 646d7e3f4..73b63f087 100644 --- a/src/components/Core/LinkedCardGrid/index.tsx +++ b/src/components/Core/LinkedCardGrid/index.tsx @@ -25,7 +25,7 @@ const GridItem = ({ title, text, children, link }: BaseBlock & { children: React
{children}
- + {title} {text} diff --git a/src/components/Core/Masthead/index.tsx b/src/components/Core/Masthead/index.tsx index 2d366136f..87bcb3123 100644 --- a/src/components/Core/Masthead/index.tsx +++ b/src/components/Core/Masthead/index.tsx @@ -22,7 +22,7 @@ export const Masthead = ({ title, buttons, caption, image }: BaseBlock): ReactEl className={css.chip} variant="outlined" /> - + {title} diff --git a/src/components/Ecosystem/Cards/index.tsx b/src/components/Ecosystem/Cards/index.tsx index e72c1ce36..133b10839 100644 --- a/src/components/Ecosystem/Cards/index.tsx +++ b/src/components/Ecosystem/Cards/index.tsx @@ -17,7 +17,7 @@ const Cards = ({ items, stacked = false }: Pick & { stacked? className={clsx(css.card, stacked && css.stacked)} >
- + {title} {image && {image.alt}} diff --git a/src/components/Governance/Delegates/index.tsx b/src/components/Governance/Delegates/index.tsx new file mode 100644 index 000000000..00216592f --- /dev/null +++ b/src/components/Governance/Delegates/index.tsx @@ -0,0 +1,87 @@ +import { Avatar, Container, Grid, IconButton, SvgIcon, Typography } from '@mui/material' +import React from 'react' +import XIcon from '@/public/images/x-icon.svg' +import DiscordIcon from '@/public/images/discord-icon.svg' +import css from './styles.module.css' +import type { BaseBlock } from '@/components/Home/types' +import { shortenAddress } from '@/lib/shortenAddress' +import HeaderCTA from '@/components/common/HeaderCTA' +import layoutCss from '@/components/common/styles.module.css' +import delegatesData from '@/content/delegates.json' +import { getDelegateImage } from '@/lib/getDelegateImage' + +type Delegate = + | { + name: string + address: string + ens: string | null + image: string + reason: string + contribution: string + } & { + twitter?: string + discord?: string + } + +const DelegateCard = (props: Delegate) => ( +
+
+ +   + + +
+ + {props.name} + + + + {props.ens || shortenAddress(props.address)} + +
+
+ + + {props.reason} + + +
+ {props.twitter && ( + + + + )} + {props.discord && ( + + + + )} +
+
+) + +const Delegates = (props: BaseBlock & { footer: { text: string; highlight: string } }) => { + return ( + + + + + {delegatesData.map((item, index) => ( + + + + ))} +
+ + +
+ + {props.footer.text} + {props.footer.highlight} + +
+ + ) +} + +export default Delegates diff --git a/src/components/Governance/Delegates/styles.module.css b/src/components/Governance/Delegates/styles.module.css new file mode 100644 index 000000000..073f230cd --- /dev/null +++ b/src/components/Governance/Delegates/styles.module.css @@ -0,0 +1,110 @@ +.card { + border: 1px solid var(--mui-palette-border-light); + border-radius: 12px; + padding: 24px; + display: flex; + flex-direction: column; + align-items: flex-start; + position: relative; + transition: 0.3s; + height: 206px; +} + +.cardHeader { + display: flex; + gap: 16px; + margin-bottom: 16px; +} + +.avatar { + width: 48px; + height: 48px; + margin: auto; +} + +.name { + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; +} + +.description { + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} + +.socials { + position: absolute; + bottom: 24px; + left: 24px; + display: flex; + gap: 16px; +} + +.socials a { + padding: 0; +} + +.socials svg { + width: 24px; + height: 24px; +} + +.gradient { + position: absolute; + height: 15%; + bottom: 0; + left: 16px; + right: 0px; + background: linear-gradient(180deg, rgba(18, 19, 18, 0) 0%, var(--mui-palette-background-default) 100%); + z-index: 1; +} + +.footer { + margin: 0 auto; +} + +/* Footer highlight */ +.footer :global span { + color: var(--mui-palette-text-primary); + text-decoration: underline; + text-underline-offset: 8px; +} + +.cardGrid :global(.MuiGrid-item):nth-of-type(9) .socials { + display: none; +} + +@media (min-width: 600px) { + .card { + height: 228px; + } +} + +@media (min-width: 900px) { + .gradient { + left: 30px; + height: 33%; + } + + .cardGrid :global(.MuiGrid-item):nth-of-type(n + 7) .socials { + display: none; + } + + .footer { + margin: 68px auto 0; + } + + .socials { + margin-top: 24px; + } +} + +@media (min-width: 1630px) { + .gradient { + left: 50px; + } +} diff --git a/src/components/Governance/Intro/index.tsx b/src/components/Governance/Intro/index.tsx new file mode 100644 index 000000000..96cb7e47f --- /dev/null +++ b/src/components/Governance/Intro/index.tsx @@ -0,0 +1,27 @@ +import { Container, Divider, Grid, Typography } from '@mui/material' +import type { BaseBlock } from '@/components/Home/types' +import css from './styles.module.css' +import ButtonsWrapper from '@/components/common/ButtonsWrapper' + +const Intro = ({ title, buttons, image }: BaseBlock) => { + return ( + + + + + {title} + + + + +
+ +
+
+
+ +
+ ) +} + +export default Intro diff --git a/src/components/Governance/Intro/styles.module.css b/src/components/Governance/Intro/styles.module.css new file mode 100644 index 000000000..c24d93562 --- /dev/null +++ b/src/components/Governance/Intro/styles.module.css @@ -0,0 +1,62 @@ +.container { + margin-top: 0px; + margin-bottom: 75px; + justify-content: space-between; + flex-direction: column-reverse; +} + +.imageContainer { + position: relative; + overflow: hidden; + height: 400px; +} + +.imageWrapper { + margin-top: 40px; + transform: scale(1.8); +} + +@media (min-width: 600px) { + .container { + margin-top: 100px; + margin-bottom: 180px; + } + + .imageWrapper { + margin-top: 0; + } +} + +@media (min-width: 900px) { + .container { + flex-direction: row; + } + + .imageContainer { + height: 500px; + } + + .imageWrapper { + transform: scale(1); + position: absolute; + z-index: -1; + width: 270%; + left: -85%; + } +} + +@media (min-width: 1200px) { + .imageWrapper { + width: 240%; + left: -70%; + top: -10%; + } +} + +@media (min-width: 1630px) { + .imageWrapper { + width: 180%; + left: -50%; + top: -20%; + } +} diff --git a/src/components/Governance/OurMission/index.tsx b/src/components/Governance/OurMission/index.tsx new file mode 100644 index 000000000..f4e6c8633 --- /dev/null +++ b/src/components/Governance/OurMission/index.tsx @@ -0,0 +1,45 @@ +import type { ReactNode } from 'react' +import type { BaseBlock } from '@/components/Home/types' +import HeaderCTA from '@/components/common/HeaderCTA' +import { Container, Grid, Typography } from '@mui/material' +import layoutCss from '@/components/common/styles.module.css' +import css from './styles.module.css' + +const GridItemBigTitle = ({ title, text, children }: Partial & { children?: ReactNode }) => { + return ( + +
+ + {title} + + {text} +
+ {children ?
{children}
: null} +
+ ) +} + +const OurMission = (props: BaseBlock) => { + return ( + + + + + {props.items?.map((item, index) => ( + + {item.image ? {item.image.alt} : null} + {item.link ? ( +
+ + {item.link.title} + + + ) : null} + + ))} + + + ) +} + +export default OurMission diff --git a/src/components/Governance/OurMission/styles.module.css b/src/components/Governance/OurMission/styles.module.css new file mode 100644 index 000000000..dc77ea58f --- /dev/null +++ b/src/components/Governance/OurMission/styles.module.css @@ -0,0 +1,53 @@ +.gradientEmphasis b { + background: linear-gradient(90deg, #00c9ff 0%, #92fe9d 100%); + -webkit-background-clip: text; + color: transparent; +} + +.card { + border: 1px solid var(--mui-palette-border-light); + transition: 0.3s; + padding: 24px; + display: flex; + flex-direction: column; + justify-content: space-between; + position: relative; +} + +.card:first-of-type { + border-top-left-radius: 12px; + border-top-right-radius: 12px; +} + +.card:last-of-type { + border-bottom-left-radius: 12px; + border-bottom-right-radius: 12px; +} + +.childrenWrapper { + margin-top: 40px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.tagline { + color: var(--mui-palette-text-primary); + text-decoration: underline; + text-underline-offset: 8px; +} + +@media (min-width: 600px) { + .card:first-of-type { + border-top-right-radius: 0px; + border-top-left-radius: 12px; + border-bottom-left-radius: 12px; + margin-right: -1px; + } + + .card:last-of-type { + border-bottom-left-radius: 0px; + border-top-right-radius: 12px; + border-bottom-right-radius: 12px; + } +} diff --git a/src/components/Governance/ParallaxDaoStats/ParallaxDaoStatsElement.tsx b/src/components/Governance/ParallaxDaoStats/ParallaxDaoStatsElement.tsx new file mode 100644 index 000000000..46a7e22ad --- /dev/null +++ b/src/components/Governance/ParallaxDaoStats/ParallaxDaoStatsElement.tsx @@ -0,0 +1,26 @@ +import Image from 'next/image' +import FrameImage from '@/public/images/Governance/Parallaxes/DaoStats/background.svg' +import ProposalsImage from '@/public/images/Governance/Parallaxes/DaoStats/proposals.png' +import DelegatesImage from '@/public/images/Governance/Parallaxes/DaoStats/delegates.png' +import DelegatorsImage from '@/public/images/Governance/Parallaxes/DaoStats/delegators.png' +import ParallaxWrapper from '@/components/common/ParallaxWrapper' +import css from './styles.module.css' + +const ParallaxDaoStatsElement = () => { + return ( +
+ + + 8 proposals + + + 2.5K delegates + + + 11.2K delegators + +
+ ) +} + +export default ParallaxDaoStatsElement diff --git a/src/components/Governance/ParallaxDaoStats/index.tsx b/src/components/Governance/ParallaxDaoStats/index.tsx new file mode 100644 index 000000000..ea189a2de --- /dev/null +++ b/src/components/Governance/ParallaxDaoStats/index.tsx @@ -0,0 +1,12 @@ +import ParallaxDaoStatsElement from '@/components/Governance/ParallaxDaoStats/ParallaxDaoStatsElement' +import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText' + +const ParallaxDaoStats = (props: ParallaxTextProps) => { + return ( + + + + ) +} + +export default ParallaxDaoStats diff --git a/src/components/Governance/ParallaxDaoStats/styles.module.css b/src/components/Governance/ParallaxDaoStats/styles.module.css new file mode 100644 index 000000000..7201cb00b --- /dev/null +++ b/src/components/Governance/ParallaxDaoStats/styles.module.css @@ -0,0 +1,60 @@ +.parallaxWrapper { + position: relative; + text-align: center; + margin: 0 auto; + width: 342px; +} + +.baseImage { + width: 100%; + height: 290px; +} + +.proposals { + position: absolute; + bottom: 242px; + left: 60px; + width: 144px; +} + +.delegates { + position: absolute; + bottom: 150px; + left: 190px; + width: 162px; +} + +.delegators { + position: absolute; + bottom: 0px; + left: 126px; + width: 162px; +} + +@media (min-width: 600px) { + .parallaxWrapper { + width: 570px; + } + + .baseImage { + height: 404px; + } + + .proposals { + bottom: 333px; + left: 75px; + width: 226px; + } + + .delegates { + bottom: 207px; + left: 275px; + width: 227px; + } + + .delegators { + bottom: 27px; + left: 160px; + width: 226px; + } +} diff --git a/src/components/Governance/ParallaxGovernanceProcess/ParallaxGovernanceProcessElement.tsx b/src/components/Governance/ParallaxGovernanceProcess/ParallaxGovernanceProcessElement.tsx new file mode 100644 index 000000000..de55d6f0e --- /dev/null +++ b/src/components/Governance/ParallaxGovernanceProcess/ParallaxGovernanceProcessElement.tsx @@ -0,0 +1,34 @@ +import Image from 'next/image' +import FrameImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/background.svg' +import LeftLineImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/leftLine.svg' +import RightLineImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/rightLine.svg' +import StepZeroImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/0.png' +import StepOneImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/1.png' +import StepTwoImage from '@/public/images/Governance/Parallaxes/GovernanceProcess/2.png' +import ParallaxWrapper from '@/components/common/ParallaxWrapper' +import css from './styles.module.css' + +const ParallaxGovernanceProcessElement = () => { + return ( +
+ + + + + + + + + Step zero + + + Step one + + + Step two + +
+ ) +} + +export default ParallaxGovernanceProcessElement diff --git a/src/components/Governance/ParallaxGovernanceProcess/index.tsx b/src/components/Governance/ParallaxGovernanceProcess/index.tsx new file mode 100644 index 000000000..549c59a4b --- /dev/null +++ b/src/components/Governance/ParallaxGovernanceProcess/index.tsx @@ -0,0 +1,12 @@ +import ParallaxGovernanceProcessElement from '@/components/Governance/ParallaxGovernanceProcess/ParallaxGovernanceProcessElement' +import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText' + +const ParallaxGovernanceProcess = (props: ParallaxTextProps) => { + return ( + + + + ) +} + +export default ParallaxGovernanceProcess diff --git a/src/components/Governance/ParallaxGovernanceProcess/styles.module.css b/src/components/Governance/ParallaxGovernanceProcess/styles.module.css new file mode 100644 index 000000000..6d265602f --- /dev/null +++ b/src/components/Governance/ParallaxGovernanceProcess/styles.module.css @@ -0,0 +1,88 @@ +.parallaxWrapper { + position: relative; + text-align: center; + margin: 0 auto; + width: 342px; +} + +.baseImage { + width: 100%; + height: 240px; +} + +.leftLine { + position: absolute; + width: 77px; + bottom: 83px; + left: 77px; +} + +.rightLine { + position: absolute; + width: 92px; + bottom: 60px; + left: 170px; +} + +.stepZero { + position: absolute; + bottom: 149px; + left: 36px; + width: 41px; +} + +.stepOne { + position: absolute; + bottom: 72px; + left: 130px; + width: 41px; +} + +.stepTwo { + position: absolute; + bottom: 164px; + left: 262px; + width: 41px; +} + +@media (min-width: 600px) { + .parallaxWrapper { + width: 570px; + } + + .baseImage { + height: 404px; + } + + .leftLine { + position: absolute; + width: 128px; + bottom: 180px; + left: 104px; + } + + .rightLine { + position: absolute; + width: 154px; + bottom: 150px; + left: 270px; + } + + .stepZero { + bottom: 253px; + left: 36px; + width: 68px; + } + + .stepOne { + bottom: 114px; + left: 205px; + width: 68px; + } + + .stepTwo { + bottom: 276px; + left: 423px; + width: 68px; + } +} diff --git a/src/components/Governance/Proposals/Proposals.tsx b/src/components/Governance/Proposals/Proposals.tsx new file mode 100644 index 000000000..23cd0cbea --- /dev/null +++ b/src/components/Governance/Proposals/Proposals.tsx @@ -0,0 +1,76 @@ +import { Chip, Container, Stack, Typography } from '@mui/material' +import type { BaseBlock } from '@/components/Home/types' +import HeaderCTA from '@/components/common/HeaderCTA' +import { parseSnapshotTitle } from '@/lib/parseSnapshotTitle' +import { useSafeSnapshot } from '@/hooks/useSafeSnapshot' +import layoutCss from '@/components/common/styles.module.css' +import css from './styles.module.css' + +const PROPOSAL_LINK_BASE_URL = 'https://snapshot.org/#/safe.eth/proposal/' + +type SnapshotProposal = { + id: string + title: string + state: 'active' | 'closed' | 'pending' + author?: string +} + +const colors: Record = { + pending: '#FF8061', + active: '#00B460', + closed: '#52BFDC', +} + +const ProposalRow = ({ title, state, href }: Omit & { href: string }) => { + const [sepNumber, description] = parseSnapshotTitle(title) + + return ( + + + {`SEP ${sepNumber}`} + {description} + + + {state} + + } + className={css.proposalChip} + variant="outlined" + sx={{ + borderColor: colors[state], + backgroundColor: colors[state], + }} + /> + + ) +} + +const Proposals = (props: BaseBlock) => { + const { data: proposals } = useSafeSnapshot() + + return ( + + + + + {proposals?.map((proposal) => ( + + ))} + + + ) +} + +export default Proposals diff --git a/src/components/Governance/Proposals/index.tsx b/src/components/Governance/Proposals/index.tsx new file mode 100644 index 000000000..ea0ea2397 --- /dev/null +++ b/src/components/Governance/Proposals/index.tsx @@ -0,0 +1,5 @@ +import dynamic from 'next/dynamic' + +const Proposals = dynamic(() => import('./Proposals')) + +export default Proposals diff --git a/src/components/Governance/Proposals/styles.module.css b/src/components/Governance/Proposals/styles.module.css new file mode 100644 index 000000000..cdc885c74 --- /dev/null +++ b/src/components/Governance/Proposals/styles.module.css @@ -0,0 +1,27 @@ +.proposalRow { + border: 1px solid var(--mui-palette-border-light); + border-radius: 12px; + padding: 24px; + + display: flex; + flex-direction: column; + gap: 8px; + + transition: 0.3s; +} + +.proposalRow:hover { + border-color: var(--mui-palette-primary-main); +} + +.proposalChip { + margin: auto 0; + width: fit-content; +} + +@media (min-width: 600px) { + .proposalRow { + flex-direction: row; + justify-content: space-between; + } +} diff --git a/src/components/Governance/ResourceHub/index.tsx b/src/components/Governance/ResourceHub/index.tsx new file mode 100644 index 000000000..4b6fc6c41 --- /dev/null +++ b/src/components/Governance/ResourceHub/index.tsx @@ -0,0 +1,22 @@ +import type { BaseBlock } from '@/components/Home/types' +import HeaderCTA from '@/components/common/HeaderCTA' +import { GridItem } from '@/components/common/UspBlock' +import { Container, Grid } from '@mui/material' +import css from './styles.module.css' +import layoutCss from '@/components/common/styles.module.css' + +const ResourceHub = ({ items, ...props }: BaseBlock) => { + return ( + + + + + {items?.map((item, index) => ( + + ))} + + + ) +} + +export default ResourceHub diff --git a/src/components/Governance/ResourceHub/styles.module.css b/src/components/Governance/ResourceHub/styles.module.css new file mode 100644 index 000000000..bb38c709d --- /dev/null +++ b/src/components/Governance/ResourceHub/styles.module.css @@ -0,0 +1,5 @@ +.gridContainer { + border: 1px solid var(--mui-palette-border-light); + border-radius: 12px; + overflow: hidden; +} diff --git a/src/components/Governance/index.tsx b/src/components/Governance/index.tsx new file mode 100644 index 000000000..2f7a60696 --- /dev/null +++ b/src/components/Governance/index.tsx @@ -0,0 +1,6 @@ +import PageContent from '@/components/common/PageContent' +import governanceContent from '@/content/governance.json' + +export const Governance = () => { + return +} diff --git a/src/components/Home/ContractParallax/index.tsx b/src/components/Home/ContractParallax/index.tsx index aae41dea8..8ab33c11f 100644 --- a/src/components/Home/ContractParallax/index.tsx +++ b/src/components/Home/ContractParallax/index.tsx @@ -1,6 +1,6 @@ import type { ReactElement } from 'react' import ContractParallaxElement from './ContractParallaxElement' -import ParallaxText, { type ParallaxTextProps } from '@/components/Wallet/ParallaxText' +import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText' import layoutCss from '@/components/common/styles.module.css' const ContractParallax = (props: ParallaxTextProps): ReactElement => { diff --git a/src/components/Home/TitleLogos/index.tsx b/src/components/Home/TitleLogos/index.tsx deleted file mode 100644 index 9e02a8d07..000000000 --- a/src/components/Home/TitleLogos/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Container, Grid, Typography } from '@mui/material' -import type { ReactElement } from 'react' - -import layoutCss from '@/components/common/styles.module.css' -import type { BaseBlock } from '@/components/Home/types' - -const TitleLogos = ({ title, items }: BaseBlock): ReactElement => { - return ( - -
- - {title} - - - - {items && - items.map((item, index) => ( - - - - ))} - - -
-
- ) -} - -export default TitleLogos diff --git a/src/components/Home/TitleTextLinkedCardGrid/index.tsx b/src/components/Home/TitleTextLinkedCardGrid/index.tsx index 5084fc60e..54c90b76f 100644 --- a/src/components/Home/TitleTextLinkedCardGrid/index.tsx +++ b/src/components/Home/TitleTextLinkedCardGrid/index.tsx @@ -38,9 +38,7 @@ const TitleTextLinkedCardGrid = ({ title, text, caption, items }: BaseBlock): Re {item.caption} - - {item.title} - + {item.title}
diff --git a/src/components/Wallet/Intro/index.tsx b/src/components/Wallet/Intro/index.tsx index fd4b1d0b1..5982af59a 100644 --- a/src/components/Wallet/Intro/index.tsx +++ b/src/components/Wallet/Intro/index.tsx @@ -7,7 +7,7 @@ import type { BaseBlock } from '@/components/Home/types' import css from './styles.module.css' import HeaderParticles from '@/public/images/header_particles.svg' -const Intro = ({ image, title, buttons }: BaseBlock & { gif: BaseBlock['image'] }): ReactElement => { +const Intro = ({ image, title, buttons }: BaseBlock): ReactElement => { return ( ( diff --git a/src/components/Wallet/OwnershipParallax/index.tsx b/src/components/Wallet/OwnershipParallax/index.tsx index c634d5542..49016d48c 100644 --- a/src/components/Wallet/OwnershipParallax/index.tsx +++ b/src/components/Wallet/OwnershipParallax/index.tsx @@ -1,4 +1,4 @@ -import ParallaxText, { type ParallaxTextProps } from '@/components/Wallet/ParallaxText' +import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText' import OwnershipParallaxElement from '@/components/Wallet/OwnershipParallax/OwnershipParallaxElement' const OwnershipParallax = (props: ParallaxTextProps) => ( diff --git a/src/components/Wallet/ParallaxAppStore/index.tsx b/src/components/Wallet/ParallaxAppStore/index.tsx index 760b5eaf4..01eaf7dc3 100644 --- a/src/components/Wallet/ParallaxAppStore/index.tsx +++ b/src/components/Wallet/ParallaxAppStore/index.tsx @@ -1,5 +1,5 @@ import ParallaxAppStoreElement from '@/components/Wallet/ParallaxAppStore/ParallaxAppStoreElement' -import ParallaxText, { type ParallaxTextProps } from '@/components/Wallet/ParallaxText' +import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText' const ParallaxAppStore = (props: ParallaxTextProps) => { return ( diff --git a/src/components/Wallet/ParallaxBatching/index.tsx b/src/components/Wallet/ParallaxBatching/index.tsx index 7df27efe1..d85eb03e4 100644 --- a/src/components/Wallet/ParallaxBatching/index.tsx +++ b/src/components/Wallet/ParallaxBatching/index.tsx @@ -1,5 +1,5 @@ import ParallaxBatchingElement from '@/components/Wallet/ParallaxBatching/ParallaxBatchingElement' -import ParallaxText, { type ParallaxTextProps } from '@/components/Wallet/ParallaxText' +import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText' const ParallaxBatching = (props: ParallaxTextProps) => { return ( diff --git a/src/components/Wallet/ParallaxSignerWallets/index.tsx b/src/components/Wallet/ParallaxSignerWallets/index.tsx index f0434893e..fe4445357 100644 --- a/src/components/Wallet/ParallaxSignerWallets/index.tsx +++ b/src/components/Wallet/ParallaxSignerWallets/index.tsx @@ -1,5 +1,5 @@ import ParallaxSignerWalletsElement from '@/components/Wallet/ParallaxSignerWallets/ParallaxSignerWalletsElement' -import ParallaxText, { type ParallaxTextProps } from '@/components/Wallet/ParallaxText' +import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText' const ParallaxSignerWallets = (props: ParallaxTextProps) => { return ( diff --git a/src/components/Wallet/ParallaxTxSimulation/index.tsx b/src/components/Wallet/ParallaxTxSimulation/index.tsx index eaac65343..a2b558b5d 100644 --- a/src/components/Wallet/ParallaxTxSimulation/index.tsx +++ b/src/components/Wallet/ParallaxTxSimulation/index.tsx @@ -1,5 +1,5 @@ import ParallaxTxSimulationElement from '@/components/Wallet/ParallaxTxSimulation/ParallaxTxSimulationElement' -import ParallaxText, { type ParallaxTextProps } from '@/components/Wallet/ParallaxText' +import ParallaxText, { type ParallaxTextProps } from '@/components/common/ParallaxText' const ParallaxTxSimulation = (props: ParallaxTextProps) => { return ( diff --git a/src/components/common/BannerGradientImage/index.tsx b/src/components/common/BannerGradientImage/index.tsx new file mode 100644 index 000000000..f54bc2310 --- /dev/null +++ b/src/components/common/BannerGradientImage/index.tsx @@ -0,0 +1,32 @@ +import { Chip, Grid, Typography } from '@mui/material' +import { Container } from '@mui/system' +import ButtonsWrapper from '@/components/common/ButtonsWrapper' +import type { BaseBlock } from '@/components/Home/types' +import layoutCss from '@/components/common/styles.module.css' +import css from './styles.module.css' + +export const BannerGradientImage = ({ title, buttons, caption, text, image }: BaseBlock) => { + return ( + +
+ + + {caption}} + className={css.chip} + variant="outlined" + /> + + {title} + + {text} + + + {image ? {image.alt} : null} + +
+
+ ) +} + +export default BannerGradientImage diff --git a/src/components/common/BannerGradientImage/styles.module.css b/src/components/common/BannerGradientImage/styles.module.css new file mode 100644 index 000000000..cb015f3a2 --- /dev/null +++ b/src/components/common/BannerGradientImage/styles.module.css @@ -0,0 +1,52 @@ +.container { + text-align: center; + border-radius: 24px; + color: var(--mui-palette-text-dark); + padding: 72px 16px; + justify-content: space-between; + position: relative; + background: rgba(15, 255, 128, 1); /* fallback */ + background-image: linear-gradient(-45deg, rgba(15, 255, 128, 1) 0%, rgba(94, 221, 255, 1) 100%); +} + +.image { + display: none; +} + +.chip { + height: 24px; + width: fit-content; + margin-top: -10px; + border-color: var(--mui-palette-text-dark); +} + +.chip span { + color: var(--mui-palette-text-dark); +} + +@media (min-width: 900px) { + .container { + text-align: left; + padding: 72px 104px; + } + + .image { + display: block; + position: absolute; + top: 0; + right: -150px; + } +} + +@media (min-width: 1200px) { + .image { + right: 0; + } +} + +@media (min-width: 1440px) { + .container { + margin-left: -104px; + margin-right: -104px; + } +} diff --git a/src/components/common/Footer/index.tsx b/src/components/common/Footer/index.tsx index 4df89eb1a..9239e7aa7 100644 --- a/src/components/common/Footer/index.tsx +++ b/src/components/common/Footer/index.tsx @@ -147,7 +147,7 @@ const Footer = () => { } return ( - + diff --git a/src/components/common/Footer/styles.module.css b/src/components/common/Footer/styles.module.css index f0b1b0672..8d4ad71ef 100644 --- a/src/components/common/Footer/styles.module.css +++ b/src/components/common/Footer/styles.module.css @@ -1,3 +1,7 @@ +.wrapper { + margin-top: 80px; +} + .list { padding: 0; list-style: none; diff --git a/src/components/common/Header/index.tsx b/src/components/common/Header/index.tsx index 333277722..273c0f9bb 100644 --- a/src/components/common/Header/index.tsx +++ b/src/components/common/Header/index.tsx @@ -4,11 +4,12 @@ import Link from 'next/link' import { AppRoutes } from '@/config/routes' import Logo from '@/public/images/logo.svg' -import { GOVERNANCE_LINK, WALLET_LINK } from '@/config/constants' +import { WALLET_LINK } from '@/config/constants' import { useOpenPositions } from '@/hooks/useOpenPositions' import css from './styles.module.css' import clsx from 'clsx' import { useRouter } from 'next/router' +import navItemsData from '@/content/navItems.json' type NavItemType = { label: string | JSX.Element @@ -16,29 +17,7 @@ type NavItemType = { external?: boolean } -const navItems: NavItemType[] = [ - { - label: 'Core', - href: AppRoutes.core, - }, - { - label: 'Wallet', - href: AppRoutes.wallet, - }, - { - label: 'Ecosystem', - href: AppRoutes.ecosystem, - }, - { - label: 'Governance', - href: GOVERNANCE_LINK, - external: true, - }, - { - label: 'Careers', - href: AppRoutes.careers, - }, -] +const navItems: NavItemType[] = navItemsData const externalLinkAttrs = { target: '_blank', diff --git a/src/components/common/HeaderCTA/index.tsx b/src/components/common/HeaderCTA/index.tsx new file mode 100644 index 000000000..6ec78c117 --- /dev/null +++ b/src/components/common/HeaderCTA/index.tsx @@ -0,0 +1,29 @@ +import type { BaseBlock } from '@/components/Home/types' +import LinkButton from '@/components/common/LinkButton' +import { Grid, Typography } from '@mui/material' +import Link from 'next/link' +import css from './styles.module.css' + +const HeaderCTA = (props: BaseBlock & { bigTitle?: boolean }) => { + return ( + + + + {props.title} + + + {props.text} + + + {props.link && ( + + + {props.link.title} + + + )} + + ) +} + +export default HeaderCTA diff --git a/src/components/common/HeaderCTA/styles.module.css b/src/components/common/HeaderCTA/styles.module.css new file mode 100644 index 000000000..5b131789f --- /dev/null +++ b/src/components/common/HeaderCTA/styles.module.css @@ -0,0 +1,25 @@ +.bigTitle { + font-size: 64px; + line-height: 64px; +} + +.linkButton { + margin-top: 24px; +} + +.shortPadding { + padding: 8px 0 !important; +} + +@media (min-width: 900px) { + .alignEnd { + display: flex; + align-items: end; + justify-content: end; + } + + .bigTitle { + font-size: 80px; + line-height: 88px; + } +} diff --git a/src/components/common/ItemGrid/index.tsx b/src/components/common/ItemGrid/index.tsx index 9a71c0eff..17dd3cfca 100644 --- a/src/components/common/ItemGrid/index.tsx +++ b/src/components/common/ItemGrid/index.tsx @@ -16,7 +16,7 @@ const ItemGrid = ({ title, items }: BaseBlock) => { ? items.map(({ title, text, link }, index) => (
- + {title} diff --git a/src/components/Wallet/ParallaxText/index.tsx b/src/components/common/ParallaxText/index.tsx similarity index 82% rename from src/components/Wallet/ParallaxText/index.tsx rename to src/components/common/ParallaxText/index.tsx index 0e3b00273..0c339d897 100644 --- a/src/components/Wallet/ParallaxText/index.tsx +++ b/src/components/common/ParallaxText/index.tsx @@ -9,6 +9,7 @@ import ButtonsWrapper from '@/components/common/ButtonsWrapper' export type ParallaxTextProps = BaseBlock & { variant: 'image-text' | 'text-image' + mobileVariant?: 'image-text' | 'text-image' } const ParallaxText = ({ @@ -18,13 +19,18 @@ const ParallaxText = ({ steps, caption, variant, + mobileVariant = 'image-text', children, }: ParallaxTextProps & { children: ReactNode; steps?: StepsType }) => { return ( - + diff --git a/src/components/Wallet/ParallaxText/styles.module.css b/src/components/common/ParallaxText/styles.module.css similarity index 79% rename from src/components/Wallet/ParallaxText/styles.module.css rename to src/components/common/ParallaxText/styles.module.css index 5e3a34ed0..f81f38827 100644 --- a/src/components/Wallet/ParallaxText/styles.module.css +++ b/src/components/common/ParallaxText/styles.module.css @@ -3,6 +3,10 @@ flex-direction: column-reverse; } +.textFirstMobile { + flex-direction: column; +} + @media (min-width: 600px) { .textFirst { flex-direction: row; diff --git a/src/components/common/TitleLogos/index.tsx b/src/components/common/TitleLogos/index.tsx new file mode 100644 index 000000000..ee5f211b7 --- /dev/null +++ b/src/components/common/TitleLogos/index.tsx @@ -0,0 +1,41 @@ +import { Container, Grid, Typography } from '@mui/material' +import type { ReactElement } from 'react' +import Link from 'next/link' +import clsx from 'clsx' +import type { BaseBlock } from '@/components/Home/types' +import layoutCss from '@/components/common/styles.module.css' + +const LogoImage = ({ image, link }: { image?: BaseBlock['image']; link?: BaseBlock['link'] }): ReactElement => { + if (link?.href) { + return ( + + + + ) + } + + return +} + +const TitleLogos = ({ title, items, hideMobile }: BaseBlock & { hideMobile?: boolean }): ReactElement => { + return ( + + + {title} + + + + {items?.map((item, index) => { + return ( + + + + ) + })} + + + + ) +} + +export default TitleLogos diff --git a/src/components/common/UspBlock/index.tsx b/src/components/common/UspBlock/index.tsx index d01ab4b2d..08563f38f 100644 --- a/src/components/common/UspBlock/index.tsx +++ b/src/components/common/UspBlock/index.tsx @@ -1,21 +1,21 @@ -import { Chip, Container, Grid, Typography } from '@mui/material' +import { Container, Grid, Typography } from '@mui/material' import type { GridProps } from '@mui/material' import type { ReactElement } from 'react' import type { BaseBlock } from '@/components/Home/types' import css from './styles.module.css' import layoutCss from '@/components/common/styles.module.css' +import Link from 'next/link' +import LinkButton from '@/components/common/LinkButton' -const GridItem = ({ image, title, text, caption, width = 4 }: BaseBlock & { width: GridProps['md'] }): ReactElement => { +export const GridItem = ({ + image, + title, + text, + link, + width = 4, +}: Partial & { width: GridProps['md'] }): ReactElement => { return ( - +
{image ? : null} @@ -23,29 +23,17 @@ const GridItem = ({ image, title, text, caption, width = 4 }: BaseBlock & { widt {text}
- {caption ? ( - - {caption} -
- } - className={css.comingSoonChip} - variant="outlined" - /> - ) : null} + {link && ( + + {link.title} + + )} ) } -export type UspBlockProps = { - variant: '3-columns' | '4-columns' - title: string - text?: string - items: BaseBlock[] -} - -const UspBlock = ({ variant, title, text, items }: UspBlockProps): ReactElement => ( +// TODO: unify the ItemGrid and accept a GridItem component +const UspBlock = ({ variant, title, text, items }: BaseBlock & { variant: '3-columns' | '4-columns' }) => ( @@ -53,7 +41,7 @@ const UspBlock = ({ variant, title, text, items }: UspBlockProps): ReactElement {text && {text}} - {items.map((item, index) => ( + {items?.map((item, index) => ( ))} diff --git a/src/components/common/UspBlock/styles.module.css b/src/components/common/UspBlock/styles.module.css index 7be8645cf..785dd3cc3 100644 --- a/src/components/common/UspBlock/styles.module.css +++ b/src/components/common/UspBlock/styles.module.css @@ -11,6 +11,10 @@ margin-left: -1px; margin-bottom: -1px; text-align: left; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; } .roundCorners { @@ -20,13 +24,6 @@ } .title { - margin-top: 24px; + margin-top: 16px; margin-bottom: 16px; } - -.comingSoonChip { - border-color: var(--mui-palette-primary-main); - width: fit-content; - height: 24px; - margin-top: 24px; -} diff --git a/src/components/common/styles.module.css b/src/components/common/styles.module.css index ca3c2f3da..e664fdda8 100644 --- a/src/components/common/styles.module.css +++ b/src/components/common/styles.module.css @@ -9,7 +9,15 @@ margin-bottom: 80px; } +.hideMobile { + display: none; +} + @media (min-width: 600px) { + .hideMobile { + display: block; + } + .container { margin-top: 350px; } diff --git a/src/config/routes.ts b/src/config/routes.ts index 8ef09d9af..95a150a0d 100644 --- a/src/config/routes.ts +++ b/src/config/routes.ts @@ -12,4 +12,5 @@ export const AppRoutes = { index: '/cla', }, careers: '/careers', + governance: '/governance', } diff --git a/src/content/delegates.json b/src/content/delegates.json new file mode 100644 index 000000000..e436e7f4f --- /dev/null +++ b/src/content/delegates.json @@ -0,0 +1,82 @@ +[ + { + "name": "Abbey Titcomb", + "address": "0xea95cfb5dd624f43775b372db0ed2d8d0073e91c", + "ens": "abbeytitcomb.eth", + "image": "0xea95cfb5dd624f43775b372db0ed2d8d0073e91c_3x.png", + "reason": "I believe we have an opportunity to build more resilient open-source ecosystems with decentralized governance. I'm dedicated to stewarding communities as they figure out their own governance culture & practices.", + "contribution": "As a representative of Radicle, I've worked with the Gnosis Safe team on product integrations and collective ecosystem growth.", + "twitter": "https://twitter.com/abbey_titcomb" + }, + { + "name": "Jonah Erlich", + "address": "0x752D6E0ac152a37005e67492B61Bfe41C1FfBEc5", + "ens": null, + "image": "0x752D6E0ac152a37005e67492B61Bfe41C1FfBEc5_3x.png", + "reason": "As a Safe builder with clarity on Safe’s problems and potential, I want to be a delegate to have an active voice stewarding its development, maintenance, and protection.", + "contribution": "We built, and will continue to build, Den on top of Safe, which helps on-chain organizations build and execute transactions fast.", + "twitter": "https://twitter.com/Jierlich" + }, + { + "name": "Tarun Gupta", + "address": "0xBd3496fE269AA8Bbf685836b63b3CadEdbE2EB56", + "ens": null, + "image": "0xBd3496fE269AA8Bbf685836b63b3CadEdbE2EB56_3x.png", + "reason": "I am Founder & CEO at Coinshift and am on a mission to bring gnosis safe to the masses by building a sophisticated Treasury Management Infrastructure. 4+ years of experience in building on top of safe contracts and Happy to take active participation in the governance forums to thrive safe ecosystem", + "contribution": "Building Treasury Infrastructure on top of gnosis safe smart contracts at coinshift.", + "twitter": "https://twitter.com/tarungupta1475" + }, + { + "name": "Auryn Macmillan", + "address": "0xd714Dd60e22BbB1cbAFD0e40dE5Cfa7bBDD3F3C8", + "ens": "auryn.eth", + "image": "0xd714Dd60e22BbB1cbAFD0e40dE5Cfa7bBDD3F3C8_3x.png", + "reason": "I care deeply about the success of the Safe ecosystem.", + "contribution": "Co-founded Gnosis Guild and helped to create Zodiac (ERC-5005), which uses the Gnosis Safe's ModuleManager interface as the core of a standard for composable tooling for programmable accounts and DAOs.", + "twitter": "https://twitter.com/auryn_macmillan" + }, + { + "name": "Corbin Page", + "address": "0x869eC00FA1DC112917c781942Cc01c68521c415e", + "ens": "corbin.eth", + "image": "0x869eC00FA1DC112917c781942Cc01c68521c415e_3x.png", + "reason": "I spend a lot of time using and building on top of the Safe contracts, apps, and Zodiac. I've also been a delegate for Aave and Fei in the past. I'd love to bring that experience to the Safe community. 💪🔰", + "contribution": "Identifying bugs, opening PRs, and product/strategy recommendations.", + "twitter": "https://twitter.com/corbpage" + }, + { + "name": "Evgeny Yurtaev", + "address": "0x7e5cE10826eE167de897D262fCC9976F609ECd2B", + "ens": "evgeny.eth", + "image": "0x7e5cE10826eE167de897D262fCC9976F609ECd2B_3x.png", + "reason": "OG DeFi degen, used Gnosis before it was Safe.", + "contribution": "Integration partner, active user & feedback giver.", + "twitter": "https://twitter.com/evgeth_" + }, + { + "name": "🔥_🔥", + "address": "0xdab0d648a2a771e6952916A822dddf738b535f5A", + "ens": "fire-infra.pod.xyz", + "image": "0xdab0d648a2a771e6952916A822dddf738b535f5A_3x.png", + "reason": "🔥_🔥 is excited to help lead the Safe ecosystem towards a decentralised community centric future. Contributing to meaningful governance experimentation is what drives 🔥_🔥.", + "contribution": "Building token and governance designs across the ecosystem, often pointing towards safe infrastructure." + }, + { + "name": "Chris Whinfrey", + "address": "0xD770d242c5734a3B8028CF46D2A2e980EcE07BeA", + "ens": "whinfrey.eth", + "image": "0xD770d242c5734a3B8028CF46D2A2e980EcE07BeA_3x.png", + "reason": "The Gnosis Safe is, without a doubt, one of the most trusted and important projects on Ethereum, securing tens of billions of dollars. As a delegate, I want to help keep the Gnosis Safe secure and user-friendly as it spins out of the broader Gnosis ecosystem. You can trust me to read every single proposal, participate in every vote, and weigh in on decisions as much as possible. I have a background in smart contract security (previously audited Open Zeppelin, dYdX, Augur, and more) as well as a background in contract-based accounts (previously cofounded Authereum). I trust the Safe contracts because I have personally audited them and have an intimate understanding of contract-based accounts, much of which I learned from studying the Safe contracts during the early days Authereum. As a delegate, I pledge to always put security first in every DAO decision and will weigh in on governance decisions from a technical standpoint when needed. I have also been a Gnosis Safe user for the past 2.5 years. I personally use a Gnosis Safe to secure the majority of my funds. A Gnosis Safe secures our team's funds at Hop. A separate Safe secures a portion of Hop DAO's funds. We used a Safe to secure Authereum's funds. The Gnosis Safe is a product that I use multiple times a week, and as a delegate, you can trust me to push for user experience improvements that will drive wider adoption and secure the Gnosis Safe as the goto evm-compatible multisig wallet. Lastly, I pledge to always vote in the best interest of the Gnosis Safe DAO and community. I will vote No on proposals that do not move the Gnosis Safe's core mission forward. I will not involve my personal interests in decision making, and should a conflict of interest arise, you can expect that I will disclose it and abstain from the vote.", + "contribution": "I'm one of the founders of Hop Protocol which is integrated as a Safe app and allows users to bridge directly from their Gnosis Safe to other chains. As Hop's feature set expands, there should be many more opportunities for Hop to help with the Safe app's cross-chain experience.", + "twitter": "https://twitter.com/WhinfreyChris" + }, + { + "name": "0xAA", + "address": "0x3317AD9eDa6942b5a7BE5BA83346C0Ea82C3C26C", + "ens": "people-dao.eth", + "image": "0x3317AD9eDa6942b5a7BE5BA83346C0Ea82C3C26C_3x.png", + "reason": "I'm a core contributor at PeopleDAO. I value gnosis safe and advocate it actively.", + "contribution": "Wrote tutorials on gnosis safe for DAOs in English and Chinese.", + "twitter": "https://twitter.com/0xAA_Science" + } +] diff --git a/src/content/governance.json b/src/content/governance.json new file mode 100644 index 000000000..1f778dce8 --- /dev/null +++ b/src/content/governance.json @@ -0,0 +1,261 @@ +[ + { + "pageTitle": "Safe Core – Full stack account abstraction", + "descripton": "Shape the future of Safe", + "component": "common/MetaTags" + }, + { + "component": "Governance/Intro", + "title": "Engage.
Participate. Vote.
Shape the future of Safe.", + "buttons": [ + { + "text": "Join the discussion", + "href": "https://forum.safe.global/", + "variant": "button" + }, + { + "text": "Vote on Snapshot", + "href": "https://snapshot.org/#/safe.eth", + "variant": "link" + } + ], + "image": { + "src": "/images/Governance/particles-globe-spin.gif", + "alt": "spinning particles globe" + } + }, + { + "component": "Governance/ParallaxDaoStats", + "variant": "image-text", + "mobileVariant": "text-image", + "title": "Safe DAO is a decentralised collective stewarding the Safe ecosystem" + }, + { + "component": "Governance/Proposals", + "title": "Latest proposals", + "text": "Get involved in the latest proposals or write one of your own and let your voice be heard.", + "link": { + "title": "View all proposals", + "href": "https://snapshot.org/#/safe.eth" + } + }, + { + "component": "Governance/Delegates", + "title": "Top delegates", + "text": "Get to know the most active Safe Guardians and SafeDAO delegates in the ecosystem, ready to receive your delegation.", + "footer": { + "text": "and more than 2K delegates." + } + }, + { + "component": "Governance/ResourceHub", + "title": "Resource hub", + "link": { + "title": "Go to governance hub", + "href": "gov.safe.global" + }, + "items": [ + { + "image": { + "src": "/images/shield-check-gradient.svg", + "alt": "checked shield", + "width": "50px" + }, + "title": "What is Safe{DAO}", + "text": "SafeDAO is a decentralised collective, stewarding the thriving ecosystem around Safe smart accounts on Ethereum and other blockchains", + "link": { + "title": "Learn more", + "href": "https://www.notion.so/safe-global/Introduction-to-SafeDAO-07f87ad6c9bf456ca5d349e72e85bf3f" + } + }, + { + "image": { + "src": "/images/tokens-gradient.svg", + "alt": "stacked tokens", + "width": "50px" + }, + "title": "What is the Safe{Token}", + "text": "The Safe token represents the growing value of the Safe ecosystem", + "link": { + "title": "Learn more", + "href": "https://www.notion.so/safe-global/Safe-Token-d558d527aa1a47daab7837bdea60923d" + } + }, + { + "image": { + "src": "/images/external-link-gradient.svg", + "alt": "external link", + "width": "50px" + }, + "title": "Who are Safe{Guardians}", + "text": "Safe Guardians are active community members who have verifiable proof of commitment to the Safe vision", + "link": { + "title": "Learn more", + "href": "https://forum.safe.global/t/guardians-gather/265" + } + }, + { + "image": { + "src": "/images/teach-gradient.svg", + "alt": "teacher pointing at board", + "width": "50px" + }, + "title": "How to delegate", + "text": "Token holders can delegate their votes to any address of their choice or to Safe Guardians using the SafeDAO Governance app within Safe{Wallet} or Snapshot", + "link": { + "title": "Learn more", + "href": "https://www.notion.so/safe-global/Delegation-ce8deecb409349d2a2785b505b9d5e86" + } + }, + { + "image": { + "src": "/images/building-gradient.svg", + "alt": "building", + "width": "50px" + }, + "title": "What is the Safe Grants Program", + "text": "SGP fosters a thriving community of contributors within the Safe ecosystem by funding ideas, projects, and integrations that align with Safe", + "link": { + "title": "Learn more", + "href": "https://grants.safe.global" + } + }, + { + "image": { + "src": "/images/shield-check-gradient.svg", + "alt": "checked shield", + "width": "50px" + }, + "title": "What is the Safe Ecosystem Foundation", + "text": "SEF is a steward of SafeDAO, responsible for facilitating and administering the decisions voted in by the members of SafeDAO", + "link": { + "title": "Learn more", + "href": "https://safe-global.notion.site/Safe-Ecosystem-Foundation-700f85745fdc45589b12569e0252916b" + } + } + ] + }, + { + "component": "Governance/OurMission", + "title": "Our mission is to accelerate the transition to smart accounts", + "link": { + "title": "Read the SafeDAO constitution", + "href": "https://snapshot.org/#/safe.eth/proposal/0xbb71846f7fdfdd31033bde9cf25c880b46d50d1dda75cbf653d6a94318f96290" + }, + "items": [ + { + "title": "Vibrant ecosystem", + "text": "To foster a vibrant Safe ecosystem built on shared components and standards", + "image": { + "src": "/images/Governance/ecosystem-avatars.png", + "alt": "Ecosystem avatars" + }, + "link": { + "title": "190+ ecosystem projects", + "href": "https://safe.global/ecosystem" + } + }, + { + "title": "Resilience through decentralisation", + "text": "The Safe ecosystem including governance shall be independent from any single entity, decentralised, and permissionless" + }, + { + "title": "Tokenise value", + "text": "Mechanisms should be created to link the growth of the Safe ecosystem to the utility of the Safe token" + } + ] + }, + { + "component": "Governance/ParallaxGovernanceProcess", + "variant": "image-text", + "caption": "How we work", + "title": "Safe governance process", + "text": "The current governance process is a series of phases involving ideation, discussion, and voting. A new governance framework is in the works.", + "buttons": [ + { + "text": "Read more", + "href": "https://www.notion.so/safe-global/Governance-overview-be50e0780b7840f08b679341ea582661?pvs=4", + "variant": "link" + } + ] + }, + { + "component": "common/TitleLogos", + "hideMobile": true, + "title": "Join the conversation", + "items": [ + { + "link": { + "href": "https://x.com/safe" + }, + "image": { + "src": "/images/x-icon.svg", + "alt": "X page" + } + }, + { + "link": { + "href": "https://forum.safe.global" + }, + "image": { + "src": "/images/discourse-icon.svg", + "alt": "Discourse forum" + } + }, + { + "link": { + "href": "https://chat.safe.global" + }, + "image": { + "src": "/images/discord-icon.svg", + "alt": "Discord server" + } + }, + { + "link": { + "href": "https://www.youtube.com/@safeglobal" + }, + "image": { + "src": "/images/youtube-icon.svg", + "alt": "Youtube channel" + } + }, + { + "link": { + "href": "https://safe.mirror.xyz" + }, + "image": { + "src": "/images/mirror-icon.svg", + "alt": "Mirror blog" + } + }, + { + "link": { + "href": "https://github.com/safe-global" + }, + "image": { + "src": "/images/github-icon.svg", + "alt": "GitHub organization" + } + } + ] + }, + { + "component": "common/BannerGradientImage", + "caption": "Community", + "title": "Apply for the Safe Grants Program", + "text": "Have a project to contribute to the Safe ecosystem? Get your ideas, builds, and integrations funded by our grants program", + "buttons": [ + { + "text": "Learn more", + "href": "https://grants.safe.global", + "variant": "button", + "color": "secondary" + } + ], + "image": { + "src": "/images/banner-lines.svg", + "alt": "Safe round block lines" + } + } +] diff --git a/src/content/home.json b/src/content/home.json index e425b1d1d..9667388ae 100644 --- a/src/content/home.json +++ b/src/content/home.json @@ -222,7 +222,7 @@ } } ], - "component": "Home/TitleLogos" + "component": "common/TitleLogos" }, { "items": [ diff --git a/src/content/navItems.json b/src/content/navItems.json new file mode 100644 index 000000000..dcdea8689 --- /dev/null +++ b/src/content/navItems.json @@ -0,0 +1,22 @@ +[ + { + "label": "Core", + "href": "/core" + }, + { + "label": "Wallet", + "href": "/wallet" + }, + { + "label": "Ecosystem", + "href": "/ecosystem" + }, + { + "label": "Governance", + "href": "/governance" + }, + { + "label": "Careers", + "href": "/careers" + } +] diff --git a/src/content/wallet.json b/src/content/wallet.json index 3afe729a1..a3815dd41 100644 --- a/src/content/wallet.json +++ b/src/content/wallet.json @@ -73,10 +73,6 @@ }, { "variant": "text-image", - "image": { - "src": "/images/Wallet/safe-apps-store.png", - "alt": "Transactions require confirmations from owners" - }, "title": "Use built-in app store", "text": "The best web3 apps are accessible right from inside the Safe Wallet interface", "buttons": [ @@ -90,10 +86,6 @@ }, { "variant": "image-text", - "image": { - "src": "/images/Wallet/batch-transactions.png", - "alt": "Batching transactions" - }, "title": "Save on gas by batching multiple transactions in a few clicks", "text": "Easily batch transactions together to save on fees", "buttons": [ @@ -107,10 +99,6 @@ }, { "variant": "text-image", - "image": { - "src": "/images/Wallet/signer-types.png", - "alt": "Different wallet types" - }, "title": "Use any type of signer to control your account", "text": "Use Ledger, Trezor, Metamask or any other popular wallet as a signer on your Safe Wallet", "buttons": [ @@ -124,10 +112,6 @@ }, { "variant": "image-text", - "image": { - "src": "/images/Wallet/simulate-transactions.png", - "alt": "Simulate transactions in Tenderly" - }, "title": "Simulate transactions before they happen", "text": "Simulate transactions before sending them through. Get instant prompts and full analysis of your transaction's success or failure with a simulation report via Tenderly", "buttons": [ @@ -236,10 +220,6 @@ }, { "variant": "text-image", - "image": { - "src": "/images/Wallet/pocket-multisig.png", - "alt": "Safe mobile app" - }, "title": "Convenience of multi-sig in your pocket", "text": "Track your assets and transactions on mobile. Stay informed and sign transactions on-the-go", "steps": [ diff --git a/src/hooks/useSafeSnapshot.ts b/src/hooks/useSafeSnapshot.ts new file mode 100644 index 000000000..63b1e26a4 --- /dev/null +++ b/src/hooks/useSafeSnapshot.ts @@ -0,0 +1,81 @@ +import useSWRImmutable from 'swr/immutable' + +type ShapshotProposalVars = { + space: string + first: number + skip: number + orderBy: 'created' + orderDirection: 'desc' | 'asc' +} + +export type SnapshotProposal = { + id: string + title: string + state: 'active' | 'closed' | 'pending' + author: string +} + +type GqlResponse = { + data: { + proposals: SnapshotProposal[] + } + errors?: Error[] +} + +const getSnapshot = async (variables: ShapshotProposalVars): Promise => { + const SNAPSHOT_GQL_ENDPOINT = 'https://hub.snapshot.org/graphql' + + const query = ` + query ($first: Int, $skip: Int, $space: String, $orderBy: String, $orderDirection: OrderDirection) { + proposals( + first: $first, + skip: $skip, + orderBy: $orderBy, + orderDirection: $orderDirection + where: { space_in: [$space] }, + ) { + id + title + state + author + } + } + ` + + const { data, errors } = (await fetch(SNAPSHOT_GQL_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, + variables, + }), + }).then((res) => res.json())) as GqlResponse + + // GraphQL returns an array of errors in res.errors + if (errors) { + throw errors[0] + } + + return data.proposals +} + +export const getSafeSnapshot = (space: string): Promise => { + const PROPOSAL_AMOUNT = 4 + + return getSnapshot({ + space, + first: PROPOSAL_AMOUNT, + skip: 0, + orderBy: 'created', + orderDirection: 'desc', + }) +} + +export const useSafeSnapshot = () => { + const QUERY_KEY = 'snapshot' + const SNAPSHOT_SPACE = 'safe.eth' + + return useSWRImmutable([QUERY_KEY], () => getSafeSnapshot(SNAPSHOT_SPACE)) +} diff --git a/src/lib/__test__/parseSnapshotTitle.test.ts b/src/lib/__test__/parseSnapshotTitle.test.ts new file mode 100644 index 000000000..b43bbbf66 --- /dev/null +++ b/src/lib/__test__/parseSnapshotTitle.test.ts @@ -0,0 +1,40 @@ +import { parseSnapshotTitle } from '@/lib/parseSnapshotTitle' + +const realTitles = [ + 'SEP #1: SafeDAO Participation Agreement', + '[SEP #2] Community Initiative To Unpause Token Contract (Enabling Transferability)', + ' [SEP #3] Towards clarity on milestones before voting on enabling transferability again', + ' [SEP #4] SafeDAO Constitution', + '[SEP #5] Redistributing Unredeemed Tokens From User Airdrop Allocation', + '# [SEP #6] Safe Grants Program (SGP)', +] + +const badTitles = [ + 'SEP 6 Safe Grants Program (SGP)', // missing # before the number + 'SEP Safe Grants Program (SGP)', // missing number +] + +describe('parseSnapshotTitle', () => { + it('returns SEP number and description in a well formattted title', () => { + expect(parseSnapshotTitle(realTitles[0])).toEqual(['1', 'SafeDAO Participation Agreement']) + expect(parseSnapshotTitle(realTitles[1])).toEqual([ + '2', + 'Community Initiative To Unpause Token Contract (Enabling Transferability)', + ]) + expect(parseSnapshotTitle(realTitles[2])).toEqual([ + '3', + 'Towards clarity on milestones before voting on enabling transferability again', + ]) + expect(parseSnapshotTitle(realTitles[3])).toEqual(['4', 'SafeDAO Constitution']) + expect(parseSnapshotTitle(realTitles[4])).toEqual([ + '5', + 'Redistributing Unredeemed Tokens From User Airdrop Allocation', + ]) + expect(parseSnapshotTitle(realTitles[5])).toEqual(['6', 'Safe Grants Program (SGP)']) + }) + + it('returns 0 and the original title if the both groups are not present in the title', () => { + expect(parseSnapshotTitle(badTitles[0])).toEqual(['0', badTitles[0]]) + expect(parseSnapshotTitle(badTitles[1])).toEqual(['0', badTitles[1]]) + }) +}) diff --git a/src/lib/getDelegateImage.ts b/src/lib/getDelegateImage.ts new file mode 100644 index 000000000..de34b9419 --- /dev/null +++ b/src/lib/getDelegateImage.ts @@ -0,0 +1,3 @@ +const GUARDIANS_IMAGE_URL = 'https://safe-claiming-app-data.safe.global/guardians/images' + +export const getDelegateImage = (address: string) => `${GUARDIANS_IMAGE_URL}/${address}_1x.png` diff --git a/src/lib/parseSnapshotTitle.ts b/src/lib/parseSnapshotTitle.ts new file mode 100644 index 000000000..a4f10a59e --- /dev/null +++ b/src/lib/parseSnapshotTitle.ts @@ -0,0 +1,21 @@ +// Fist group: SEP number +// Second group: SEP title +const snapshotRegex = /SEP #(\d+)\]?[^A-Za-z]*(.*)/ + +/** + * Parses a snapshot title to extract the numeric identifier and description. + * + * @param {string} title - The snapshot title to parse. + * @returns {[string, string]} - An array containing the numeric identifier and description. + * - If both groups are not present, it returns ['0', title]. + */ +export function parseSnapshotTitle(title: string): [string, string] { + const match = title.match(snapshotRegex) + + // If both groups are not present, return undefined + if (match?.length !== 3) { + return ['0', title] + } + + return [match[1], match[2]] +} diff --git a/src/lib/shortenAddress.ts b/src/lib/shortenAddress.ts new file mode 100644 index 000000000..2218c075e --- /dev/null +++ b/src/lib/shortenAddress.ts @@ -0,0 +1,7 @@ +export const shortenAddress = (address: string, length = 4): string => { + if (!address) { + return '' + } + + return `${address.slice(0, length + 2)}...${address.slice(-length)}` +} diff --git a/src/pages/governance.tsx b/src/pages/governance.tsx new file mode 100644 index 000000000..0c494f5b6 --- /dev/null +++ b/src/pages/governance.tsx @@ -0,0 +1,8 @@ +import type { NextPage } from 'next' +import { Governance } from '@/components/Governance' + +const GovernancePage: NextPage = () => { + return +} + +export default GovernancePage diff --git a/src/styles/theme.ts b/src/styles/theme.ts index 3932eab93..0747c7f3c 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -83,15 +83,25 @@ export const theme = createTheme({ lineHeight: '36px', }, h4: { - fontSize: '24px', - lineHeight: '32px', + fontSize: '20px', + lineHeight: '30px', + + '@media (min-width:600px)': { + fontSize: '24px', + lineHeight: '32px', + }, }, h5: { fontSize: '16px', }, body1: { - fontSize: '18px', - lineHeight: '28px', + fontSize: '16px', + lineHeight: '24px', + + '@media (min-width:600px)': { + fontSize: '18px', + lineHeight: '28px', + }, }, body2: { fontSize: '14px', @@ -158,11 +168,6 @@ export const theme = createTheme({ paddingLeft: '24px', paddingRight: '24px', }, - - '& .MuiContainer-root': { - paddingLeft: 0, - paddingRight: 0, - }, }, }, },