Work on CMS (#65)

* Work on CMS
This commit is contained in:
Fede Álvarez 2022-05-18 23:57:44 +02:00 committed by GitHub
parent ebeb481e50
commit 91b47f439d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 2289 additions and 1593 deletions

View File

@ -26,6 +26,7 @@
"@types/lodash": "^4.14.182",
"@types/marked": "^4.0.3",
"clsx": "^1.1.1",
"datocms-structured-text-to-html-string": "^2.0.4",
"datocms-structured-text-to-plain-text": "^2.0.4",
"graphql": "^16.3.0",
"graphql-request": "^4.2.0",
@ -46,7 +47,8 @@
"react-merge-refs": "^1.1.0",
"react-query": "^3.35.0",
"react-use-measure": "^2.1.1",
"sharp": "^0.30.4"
"sharp": "^0.30.4",
"tiny-json-http": "^7.4.2"
},
"devDependencies": {
"@graphql-codegen/cli": "^2.6.2",
@ -55,6 +57,7 @@
"@types/node": "^17.0.25",
"@types/react": "^17.0.43",
"@types/react-dom": "^17.0.14",
"@types/tiny-json-http": "^7.3.1",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"@typescript-eslint/parser": "^5.20.0",
"cross-env": "^7.0.3",

View File

@ -1,7 +1,7 @@
import clsx from 'clsx'
import NextLink from 'next/link'
import { useRouter } from 'next/router'
import { useCallback } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { Calendar, Clock } from '~/components/icons/events'
import Heading from '~/components/primitives/heading'
@ -20,12 +20,33 @@ interface CardProps {
}
const Card = ({ className, data, isNews = false }: CardProps) => {
const [eventDate, setEventDate] = useState<string>()
const [eventTime, setEventTime] = useState<string>()
useEffect(() => {
setEventDate(
new Date(data?.eventDatetime).toLocaleDateString('default', {
year: '2-digit',
month: '2-digit',
day: '2-digit'
})
)
setEventTime(
new Date(data?.eventDatetime).toLocaleTimeString('default', {
hour: '2-digit',
minute: '2-digit'
})
)
}, [data?.eventDatetime])
return (
<NextLink href={isNews ? `/blog/${data?.slug}` : data?.link}>
<NextLink href={isNews ? `/blog/${data?.slug}` : data?.eventLink}>
<div className={clsx(s['card'], className)}>
<div className={s['card__header']}>
{!isNews && (
<span className={s.location}>{data?.location?.toUpperCase()}</span>
<span className={s.location}>
{data?.eventLocation?.toUpperCase()}
</span>
)}
<div className={isNews ? s.is_news : ''}>
{isNews && (
@ -35,12 +56,12 @@ const Card = ({ className, data, isNews = false }: CardProps) => {
)}
<span className={s.date}>
{!isNews && <Calendar />}
{data?.date}
{isNews ? data?.date : eventDate}
</span>
{!isNews && (
<span className={s.hour}>
<Clock />
{data?.time} HS
{eventTime} HS
</span>
)}
</div>
@ -49,15 +70,15 @@ const Card = ({ className, data, isNews = false }: CardProps) => {
<img
alt={data?.title}
loading="lazy"
src={isNews ? data?.image.url : data?.imgSrc}
src={isNews ? data?.image.url : data?.eventImage?.url}
/>
</div>
<div className={s['content']}>
<Heading as="h2" variant="sm" font="tthoves">
{data?.title}
{isNews ? data?.title : data?.eventTitle}
</Heading>
<p>{isNews ? getDescription(data) : data?.preview}</p>
<Link href={isNews ? `/blog/${data?.slug}` : data?.link}>
<p>{isNews ? getDescription(data) : data?.eventDesc}</p>
<Link href={isNews ? `/blog/${data?.slug}` : data?.eventLink}>
{isNews ? 'READ ARTICLE' : 'LEARN MORE'}
</Link>
</div>

View File

@ -7,7 +7,18 @@ import Link from '~/components/primitives/link'
import s from './hero.module.scss'
const Hero = () => {
interface Props {
data: {
heroCtaLabel: string
heroCtaLink: string
heroDescB01: string
heroDescB02: string
heroHeading: string
heroTitle: string
}
}
const Hero = ({ data }: Props) => {
return (
<Section className={s['section']} disableFadeIn>
<div className={s['gradient']} />
@ -34,35 +45,24 @@ const Hero = () => {
<div className={s['body']}>
<div className={s['header']}>
<Heading as="h1" variant="xl">
Accelerating Web3
{data?.heroHeading}
</Heading>
<Arrow className={s['arrow']} />
</div>
<div className={s['content']}>
<div>
<Line className={s['line']} height={145} />
<Heading as="h2" variant="sm">
Were materializing a decentralized and interoperable future,{' '}
<b>now</b>
</Heading>
<Heading
as="h2"
variant="sm"
dangerouslySetInnerHTML={{ __html: data?.heroTitle }}
/>
</div>
<div>
<p>
For blockchain to achieve true decentralization and wider
adoption, we need to expand data access, verifiability, and
control for decentralized application developers and end
consumers around the world.
</p>
<p>
Led by a globally renowned team of architects and core
contributors across Ethereum, IPLD / IPFS, and Cosmos SDK, our
mission is to accelerate blockchain development,
interoperability, and adoption by providing decentralized
application (DApp) developers and users with greater access to
verifiable data.
</p>
<p>{data?.heroDescB01}</p>
<p>{data?.heroDescB02}</p>
<div>
<Link href="https://docs.laconic.com/faq">FAQ</Link>
<Link href={data?.heroCtaLink}>{data?.heroCtaLabel}</Link>
</div>
</div>
</div>

View File

@ -5,27 +5,43 @@ import Heading from '~/components/primitives/heading'
import Team from './team'
import s from './team.module.scss'
import { cercTeam, laconicTeam, xylmTeam } from './teams'
const TeamSection = () => {
interface Props {
data: {
teamHeading: string
teamDescB01: string
teamDescB02: string
}
teamData: {
memberGithub: string
memberImage: {
url: string
}
memberLinkedin: string
memberName: string
memberPosition: string
memberTeam: string
memberTwitter: string
}[]
}
const TeamSection = ({ teamData, data }: Props) => {
const laconicTeam = teamData?.filter(
(member) => member.memberTeam === 'Laconic'
)
const cercTeam = teamData?.filter((member) => member.memberTeam === 'Cerc')
const xylmTeam = teamData?.filter((member) => member.memberTeam === 'Xylm')
return (
<Section className={s['section']} id="team">
<Container className={s['container']}>
<Heading as="h2" variant="lg">
Team
{data?.teamHeading}
</Heading>
<div>
<Arrow className={s['arrow']} />
<p>
Built over four years, Laconic is developed by platform experts
across Ethereum, IPLD / IPFS, and Cosmos SDK. Our project
organizations are led by Ethereum architects and developers who
co-authored and contributed to EIP-1559.
</p>
<p>
Weve successfully launched startups, and built products and
communities loved by millions of people around the world.
</p>
<p>{data?.teamDescB01}</p>
<p>{data?.teamDescB02}</p>
</div>
<Team members={laconicTeam} teamName="Laconic LLC" teamNumber="01" />
<Team

View File

@ -12,12 +12,14 @@ import s from './team.module.scss'
type Team = {
member: {
fullName: string
github: string
image: string
linkedin: string
position: string
twitter: string
memberName: string
memberPosition: string
memberImage: {
url: string
}
memberGithub: string
memberTwitter: string
memberLinkedin: string
}
className?: string
}
@ -25,24 +27,28 @@ type Team = {
function MemberProfile({ member, className }: Team) {
return (
<div className={clsx(s['member'], className)}>
<img alt={member.fullName} loading="lazy" src={member.image} />
<img
alt={member?.memberName}
loading="lazy"
src={member?.memberImage?.url}
/>
<div className={s['description']}>
<span>{member.fullName}</span>
<span>{member.position}</span>
<span>{member?.memberName}</span>
<span>{member?.memberPosition}</span>
</div>
<div className={s['socials']}>
{member.twitter && (
<a href={member.twitter} target="_blank" rel="noopener">
{member?.memberTwitter && (
<a href={member?.memberTwitter} target="_blank" rel="noopener">
<Twitter />
</a>
)}
{member.github && (
<a href={member.github} target="_blank" rel="noopener">
{member?.memberGithub && (
<a href={member?.memberGithub} target="_blank" rel="noopener">
<Github />
</a>
)}
{member.linkedin && (
<a href={member.linkedin} target="_blank" rel="noopener">
{member?.memberLinkedin && (
<a href={member?.memberLinkedin} target="_blank" rel="noopener">
<Linkedin />
</a>
)}
@ -53,12 +59,15 @@ function MemberProfile({ member, className }: Team) {
type Teams = {
members: {
fullName: string
github: string
image: string
linkedin: string
position: string
twitter: string
memberGithub: string
memberImage: {
url: string
}
memberLinkedin: string
memberName: string
memberPosition: string
memberTeam: string
memberTwitter: string
}[]
teamName: string
teamNumber: string

View File

@ -1,96 +0,0 @@
export const laconicTeam = [
{
fullName: 'Rick Dudley',
position: 'Chief Architect, Cerc',
image: '/images/about/team/rick-dudley.jpg',
github: 'https://github.com/AFDudley',
linkedin: 'https://www.linkedin.com/in/afdudley/',
twitter: 'https://twitter.com/AFDudley0'
},
{
fullName: 'Maly Ly',
position: 'Chief Executive, Cerc',
image: '/images/about/team/maly-ly.jpg',
github: '',
linkedin: 'https://www.linkedin.com/in/malytly/',
twitter: 'https://twitter.com/malytly'
},
{
fullName: 'Boris Mann',
position: 'CEO, Fission',
image: '/images/about/team/boris-mann.jpg',
github: 'https://github.com/bmann',
linkedin: 'https://www.linkedin.com/in/boris/',
twitter: 'https://twitter.com/bmann'
},
{
fullName: 'Brooklyn Zelenka',
position: 'CTO, Fission',
image: '/images/about/team/brooklyn-zelenka.jpg',
github: 'https://github.com/expede',
linkedin: 'https://www.linkedin.com/in/brooklynzelenka/',
twitter: 'https://twitter.com/expede'
}
]
export const cercTeam = [
{
fullName: 'Ashwin Phatak',
position: 'VP of Engineering',
image: '/images/about/team/ashwin-phatak.jpg',
twitter: 'https://twitter.com/ashwinphatak',
linkedin: 'https://www.linkedin.com/in/ashwinphatak/',
github: 'https://github.com/ashwinphatak'
},
{
fullName: 'Ian Norden',
position: 'Engineering Lead',
image: '/images/about/team/ian-norden.jpg',
twitter: '',
linkedin: 'https://www.linkedin.com/in/ian-s-norden/',
github: 'https://github.com/i-norden'
},
{
fullName: 'Leah Light',
position: 'Operations Manager',
image: '/images/about/team/leah-light.jpg',
twitter: '',
linkedin: 'https://www.linkedin.com/in/leah-light-a308535/',
github: ''
},
{
fullName: 'Erik Dies',
position: 'Product Lead',
image: '/images/about/team/erik-dies.jpg',
twitter: 'https://twitter.com/thelesserdies',
linkedin: '',
github: 'https://github.com/erikdies'
}
]
export const xylmTeam = [
{
fullName: 'Robert Douglass',
position: 'VP of Ecosystem',
image: '/images/about/team/robert-douglass.jpg',
twitter: 'https://twitter.com/robertDouglass',
linkedin: 'https://www.linkedin.com/in/roberttdouglass/',
github: ''
},
{
fullName: 'Michael Gushansky',
position: 'Head of Marketing',
image: '/images/about/team/michael-gushansky.jpg',
twitter: 'https://twitter.com/mikegushansky',
linkedin: 'https://www.linkedin.com/in/michael-gushansky-08698384/',
github: ''
},
{
fullName: 'Olive Kimoto',
position: 'Creative & Program Lead',
image: '/images/about/team/olive-kimoto.jpg',
twitter: 'https://twitter.com/olivekimoto',
linkedin: 'https://www.linkedin.com/in/olive-kimoto-71433b138/',
github: ''
}
]

View File

@ -1,42 +0,0 @@
export const events = [
{
location: 'París, France',
date: '04.13.22',
time: '08:00',
imgSrc: '/images/community/events/card01.png',
title: 'Paris Blockchain Week',
preview:
'PBW is the flagship event of Paris Blockchain Week gathering more than 3000 attendees, 70 sponsors, 250 speakers and 100 media partners.',
link: 'https://www.pbwsummit.com/'
},
{
location: 'Amsterdam, Netherlands',
date: '04.18.22',
time: '09:00',
imgSrc: '/images/community/events/card02.png',
title: 'Devconnect',
preview:
'Devconnect is a week-long in-person gathering that will feature independent Ethereum events, each with a unique focus.',
link: 'https://devconnect.org/'
},
{
location: 'Austin, TX',
date: '06.09.22',
time: '09:00',
imgSrc: '/images/community/events/card03.png',
title: 'Consensus',
preview:
'Consensus, the most influential crypto & blockchain experience of the year since 2015, will return to an in-person format and be held in Austin, Texas, for the first time ever.',
link: 'https://events.coindesk.com/consensus2022'
},
{
location: 'Amsterdam, NL',
date: '07.19.22',
time: '19:30',
imgSrc: '/images/community/events/card04.png',
title: 'ETH CC',
preview:
'The Ethereum Community Conference (EthCC) is the largest annual European Ethereum event focused on technology and community.',
link: 'https://ethcc.io/'
}
]

View File

@ -10,10 +10,27 @@ import { Container } from '~/components/layout/container'
import Section from '~/components/layout/section'
import Heading from '~/components/primitives/heading'
import { events } from './events'
import s from './events.module.scss'
const Events = () => {
interface Props {
data: {
eventsHeading: string
eventsDescription: string
}
eventsData: {
eventDatetime: string
eventDesc: string
eventImage: {
url: string
}
eventLink: string
eventLocation: string
eventTitle: string
id: string
}[]
}
const Events = ({ eventsData, data }: Props) => {
const [loaded, setLoaded] = useState(false)
const [sliderRef, slider] = useKeenSlider<HTMLDivElement>({
@ -39,11 +56,9 @@ const Events = () => {
<Container className={s['container']}>
<div className={s['header']}>
<Heading as="h2" variant="lg">
Events
{data?.eventsHeading}
</Heading>
<span>
Wed love to meet you in person. Find us at a conference near you:
</span>
<span>{data?.eventsDescription}</span>
<div className={s['gradient']} />
</div>
</Container>
@ -52,8 +67,8 @@ const Events = () => {
className={clsx(s['events__container'], 'keen-slider')}
ref={sliderRef}
>
{events.map((event, i) => (
<Card key={i} data={event} className="keen-slider__slide" />
{eventsData.map((event) => (
<Card key={event?.id} data={event} className="keen-slider__slide" />
))}
</div>
</div>

View File

@ -5,7 +5,18 @@ import Heading from '~/components/primitives/heading'
import s from './hero.module.scss'
const Hero = () => {
interface Props {
data: {
heroButton01: string
heroButton02: string
heroB01Link: string
heroB02Link: string
heroDescription: string
heroHeading: string
}
}
const Hero = ({ data }: Props) => {
return (
<Section className={s['section']} disableFadeIn>
<div className={s['gradient']}></div>
@ -30,32 +41,29 @@ const Hero = () => {
/>
<div className={s['header']}>
<Heading as="h1" variant="xl">
Laconic Community
{data?.heroHeading}
</Heading>
<Arrow className={s['arrow--mobile']} />
</div>
<div className={s['content']}>
<Arrow className={s['arrow']} />
<p>
Join us as we accelerate blockchain development and interoperability
by expanding access to verifiable data.
</p>
<p>{data?.heroDescription}</p>
<div className={s['buttons__container']}>
<ButtonLink
variant="primary"
size="large"
className={s['button']}
href="https://discord.com/invite/ukhbBemyxY"
href={data?.heroB01Link}
>
CHAT WITH US
{data?.heroButton01}
</ButtonLink>
<ButtonLink
variant="primary"
size="large"
className={s['button']}
href="https://laconic.community/"
href={data?.heroB02Link}
>
GET STARTED
{data?.heroButton02}
</ButtonLink>
</div>
</div>

View File

@ -17,7 +17,25 @@ import Heading from '~/components/primitives/heading'
import s from './socials.module.scss'
const Socials = () => {
interface Props {
data: {
socialsHeading: string
socialsLine: string
socialsImage: {
url: string
}
socialsTwitter: string
socialsDiscord: string
socialsDiscourse: string
socialsReddit: string
socialsTelegram: string
socialsLinkedin: string
socialsFacebook: string
socialsInstagram: string
}
}
const Socials = ({ data }: Props) => {
return (
<Section className={s['section']}>
<Container className={s['container']}>
@ -25,59 +43,75 @@ const Socials = () => {
alt="Planet"
className={s['planet--image']}
loading="lazy"
src="/images/community/socials/planet.png"
src={data?.socialsImage?.url}
/>
<div>
<div className={s['header']}>
<Heading as="h2" variant="xl" centered={false}>
Connect With Us
{data?.socialsHeading}
</Heading>
</div>
<Line className={s['line']} height={144} />
</div>
<div className={s['icons__container']}>
<span>GET IN TOUCH</span>
<span>{data?.socialsLine}</span>
<div className={s['grid']}>
<Link href={`https://twitter.com/laconicnetwork`}>
<a target="_blank">
<Twitter />
</a>
</Link>
<Link href={`https://discord.com/invite/ukhbBemyxY`}>
<a target="_blank">
<Discord />
</a>
</Link>
<Link href={`https://laconic.community/`}>
<a target="_blank">
<Discourse />
</a>
</Link>
<Link href={`https://www.reddit.com/r/LaconicNetwork/`}>
<a target="_blank">
<Reddit />
</a>
</Link>
<Link href={`https://t.me/laconicnetwork`}>
<a target="_blank">
<Telegram />
</a>
</Link>
<Link href={`https://www.linkedin.com/company/laconic-network/`}>
<a target="_blank">
<Linkedin />
</a>
</Link>
<Link href={`https://www.facebook.com/laconicnetwork`}>
<a target="_blank">
<Facebook />
</a>
</Link>
<Link href={`https://instagram.com/laconicnetwork`}>
<a target="_blank">
<Instagram />
</a>
</Link>
{data?.socialsTwitter && (
<Link href={data?.socialsTwitter}>
<a target="_blank">
<Twitter />
</a>
</Link>
)}
{data?.socialsDiscord && (
<Link href={data?.socialsDiscord}>
<a target="_blank">
<Discord />
</a>
</Link>
)}
{data?.socialsDiscourse && (
<Link href={data?.socialsDiscourse}>
<a target="_blank">
<Discourse />
</a>
</Link>
)}
{data?.socialsReddit && (
<Link href={data?.socialsReddit}>
<a target="_blank">
<Reddit />
</a>
</Link>
)}
{data?.socialsTelegram && (
<Link href={data?.socialsTelegram}>
<a target="_blank">
<Telegram />
</a>
</Link>
)}
{data?.socialsLinkedin && (
<Link href={data?.socialsLinkedin}>
<a target="_blank">
<Linkedin />
</a>
</Link>
)}
{data?.socialsFacebook && (
<Link href={data?.socialsFacebook}>
<a target="_blank">
<Facebook />
</a>
</Link>
)}
{data?.socialsInstagram && (
<Link href={data?.socialsInstagram}>
<a target="_blank">
<Instagram />
</a>
</Link>
)}
</div>
</div>
</Container>

View File

@ -28,12 +28,43 @@ import {
Icon12
} from './icons'
const Benefits = () => {
interface Props {
data: {
benefitsHeading: string
benefitsDesc: string
benefitsS01Desc: string
benefitsS01Heading: string
benefitsS01Item01: string
benefitsS01Item02: string
benefitsS01Item03: string
benefitsS01Item04: string
benefitsS01Item05: string
benefitsS01Item06: string
benefitsS02Desc: string
benefitsS02Heading: string
benefitsS02Item01: string
benefitsS02Item02: string
benefitsS02Item03: string
benefitsS02Item04: string
benefitsS02Item05: string
benefitsS02Item06: string
benefitsButton: string
benefitsButtonLink: string
}
}
const Benefits = ({ data }: Props) => {
const ballRef = React.useRef<HTMLDivElement>(null)
const gridRef = React.useRef<HTMLDivElement>(null)
const userRef = React.useRef<HTMLDivElement>(null)
const isMobile = useMedia('(max-width: 768px)')
const [isMounted, setIsMounted] = React.useState(false)
React.useEffect(() => {
setIsMounted(true)
}, [])
useIsomorphicLayoutEffect(() => {
if (isMobile || typeof isMobile === 'undefined') return
if (!ballRef.current) return
@ -61,24 +92,16 @@ const Benefits = () => {
<Section className={s['section']}>
<Container className={s['header']}>
<Heading as="h2" variant="lg">
Benefits
{data?.benefitsHeading}
</Heading>
<div className={s['grid']}>
<div className={s['ball']} ref={ballRef} />
<div className={s['icon']}>
<Arrow />
</div>
<p>
For DApp developers and users, Laconic is the only solution that
provides accurate, low-cost, multi-chain verifiable data, by
leveraging cryptographic proofs and cutting edge data caching
engineering. <br />
<br />{' '}
<b>
Laconic is the only blockchain data querying solution that
provides cross-chain cryptographic proofs.
</b>
</p>
{isMounted && (
<p dangerouslySetInnerHTML={{ __html: data?.benefitsDesc }} />
)}
</div>
</Container>
<img
@ -97,32 +120,17 @@ const Benefits = () => {
<span>01</span>
<Line className={s['line']} height={144} />
<Heading as="h3" variant="sm">
For DApp Developers
{data?.benefitsS01Heading}
</Heading>
<p>
Quickly and easily build blockchain applications with the fastest,
most accurate, and lowest cost multi-chain verifiable data
solution.
</p>
<p>{data?.benefitsS01Desc}</p>
</div>
<ul>
<Benefit icon={<Icon01 />}>
Only solution providing Cryptographic proofs
</Benefit>
<Benefit icon={<Icon02 />}>
Composable, supports broader use cases
</Benefit>
<Benefit icon={<Icon03 />}>
From months to minutes. Quick automated setup
</Benefit>
<Benefit icon={<Icon04 />}>Broad cross-chain compatibility</Benefit>
<Benefit icon={<Icon05 />}>
Unparalleled flexibility. Supports hash-linked data including
blockchain
</Benefit>
<Benefit icon={<Icon06 />}>
Lower development and data costs. 100x of times less than market
</Benefit>
<Benefit icon={<Icon01 />}>{data?.benefitsS01Item01}</Benefit>
<Benefit icon={<Icon02 />}>{data?.benefitsS01Item02}</Benefit>
<Benefit icon={<Icon03 />}>{data?.benefitsS01Item03}</Benefit>
<Benefit icon={<Icon04 />}>{data?.benefitsS01Item04}</Benefit>
<Benefit icon={<Icon05 />}>{data?.benefitsS01Item05}</Benefit>
<Benefit icon={<Icon06 />}>{data?.benefitsS01Item06}</Benefit>
</ul>
</Container>
<img
@ -141,37 +149,24 @@ const Benefits = () => {
<span>02</span>
<Line className={s['line']} height={112} />
<Heading as="h3" variant="sm">
For DApp Users
{data?.benefitsS02Heading}
</Heading>
<p>
Experience a higher quality of service and better uptime with
Laconic.
</p>
<p>{data?.benefitsS02Desc}</p>
</div>
<ul>
<Benefit icon={<Icon07 />}>
Greater data access and control
</Benefit>
<Benefit icon={<Icon08 />}>
In-browser local cache, without need for data availability layer
</Benefit>
<Benefit icon={<Icon09 />}>
Optimal control of privacy levels
</Benefit>
<Benefit icon={<Icon10 />}>Self-Verify data accuracy</Benefit>
<Benefit icon={<Icon11 />}>
Higher quality of service and uptime
</Benefit>
<Benefit icon={<Icon12 />}>
LNT rewards for network participation
</Benefit>
<Benefit icon={<Icon07 />}>{data?.benefitsS02Item01}</Benefit>
<Benefit icon={<Icon08 />}>{data?.benefitsS02Item02}</Benefit>
<Benefit icon={<Icon09 />}>{data?.benefitsS02Item03}</Benefit>
<Benefit icon={<Icon10 />}>{data?.benefitsS02Item04}</Benefit>
<Benefit icon={<Icon11 />}>{data?.benefitsS02Item05}</Benefit>
<Benefit icon={<Icon12 />}>{data?.benefitsS02Item06}</Benefit>
</ul>
<ButtonLink
className={s['button']}
variant="primary"
href="https://discord.com/invite/ukhbBemyxY"
href={data?.benefitsButtonLink}
>
Get Started
{data?.benefitsButton}
</ButtonLink>
</Container>
</Container>

View File

@ -5,12 +5,22 @@ import Line from '~/components/icons/line'
import Section from '~/components/layout/section'
import { ButtonLink } from '~/components/primitives/button'
import Heading from '~/components/primitives/heading'
import HighlightedText from '~/components/primitives/highlighted-text'
import { useIsomorphicLayoutEffect } from '~/hooks/use-isomorphic-layout-effect'
import s from './hero.module.scss'
const Hero = () => {
interface props {
data: {
heroHeading: string
heroButton01: string
heroB01Link: string
heroButton02: string
heroB02Link: string
heroSlogan: any
}
}
const Hero = ({ data }: props) => {
const videoRef = useRef<HTMLVideoElement>(null)
useIsomorphicLayoutEffect(() => {
@ -38,36 +48,30 @@ const Hero = () => {
>
<source src="/videos/hero-grid.mp4" type="video/mp4" />
</video>
<Heading as="h1" variant="xl" centered>
Build a <br className="hide-on-desktop" />{' '}
<HighlightedText>
Fast, Frictionless, <br /> & Provable
</HighlightedText>{' '}
Web3
</Heading>
<Heading
as="h1"
variant="xl"
centered
dangerouslySetInnerHTML={{ __html: data?.heroHeading }}
/>
<Line className={s['line']} />
<p>
From months to minutes. Laconics multi-chain verifiable data <br />{' '}
marketplace accelerates blockchain development, <br />
interoperability, and adoption
</p>
<div dangerouslySetInnerHTML={{ __html: data?.heroSlogan }} />
<div className={s['buttons__container']}>
<ButtonLink
variant="primary"
size="large"
className={s['button']}
href="https://discord.com/invite/ukhbBemyxY"
href={data?.heroB01Link}
>
JOIN US NOW
{data?.heroButton01}
</ButtonLink>
<ButtonLink
variant="default"
size="large"
className={s['button']}
href="https://laconic.community/"
href={data?.heroB02Link}
>
CHAT WITH US
{data?.heroButton02}
</ButtonLink>
</div>
</Section>

View File

@ -13,10 +13,13 @@ import s from './latest-news.module.scss'
// TODO
export interface Props {
data: any
blogData: any
data: {
latestHeading: string
}
}
const LatestNews = ({ data }: Props) => {
const LatestNews = ({ data, blogData }: Props) => {
const [loaded, setLoaded] = useState(false)
const [sliderRef, slider] = useKeenSlider<HTMLDivElement>({
@ -40,13 +43,15 @@ const LatestNews = ({ data }: Props) => {
return (
<Section className={s['section']}>
<Container className={s.heading}>
<Heading as="h2" variant="lg">
Latest news <br /> from Laconic
</Heading>
<Heading
as="h2"
variant="lg"
dangerouslySetInnerHTML={{ __html: data?.latestHeading }}
/>
</Container>
<div className={s['slider__container']}>
<div className={`${s.events__container} keen-slider`} ref={sliderRef}>
{data.map((item: any, i: Key) => (
{blogData.map((item: any, i: Key) => (
<Card key={i} data={item} className={`keen-slider__slide`} isNews />
))}
</div>

View File

@ -11,10 +11,24 @@ import Heading from '~/components/primitives/heading'
import { CUSTOM_EASE, DURATION, gsap, Observer } from '~/lib/gsap'
import Item from './item'
import { testimonials } from './testimonials'
import s from './what-others-say.module.scss'
const WhatOthersSay = () => {
interface Props {
data: {
othersHeading: string
}
testimonialsData: {
id: string
testimonialImage: {
url: string
}
testimonialPosition: string
testimonialText: string
testimonialUsername: string
}[]
}
const WhatOthersSay = ({ data, testimonialsData }: Props) => {
const carouselRef = useRef<HTMLDivElement>(null)
const { vw } = useRealViewport()
const [ref] = useKeenSlider<HTMLDivElement>({
@ -78,25 +92,26 @@ const WhatOthersSay = () => {
<Section className={s['section']}>
<Container>
<div className={s['header']}>
<Heading as="h2" variant="md" centered>
What <br className="hide-on-mobile" /> others say
</Heading>
<Heading
as="h2"
variant="md"
centered
dangerouslySetInnerHTML={{ __html: data?.othersHeading }}
/>
</div>
<div className={s['carousel']} ref={carouselRef}>
<div className={s['image']}>
<img src="/images/head.png" loading="lazy" alt="Head" />
</div>
{testimonials.map((testimonial, index) => {
{testimonialsData.map((testimonial) => {
return (
<Item
className="carousel__item"
image={testimonial.image}
key={index}
name={testimonial.name}
image={testimonial?.testimonialImage?.url}
key={testimonial?.id}
name={testimonial?.testimonialUsername}
>
{testimonial.text}
{testimonial?.testimonialText}
</Item>
)
})}
@ -106,15 +121,15 @@ const WhatOthersSay = () => {
<div className={s['image']}>
<img src="/images/head.png" loading="lazy" alt="Head" />
</div>
{testimonials.map((testimonial, index) => {
{testimonialsData.map((testimonial) => {
return (
<Item
className="keen-slider__slide"
image={testimonial.image}
key={index}
name={testimonial.name}
image={testimonial?.testimonialImage?.url}
key={testimonial?.id}
name={testimonial?.testimonialUsername}
>
{testimonial.text}
{testimonial?.testimonialText}
</Item>
)
})}

View File

@ -1,84 +0,0 @@
import Square from '~/components/logos/square'
export const testimonials = [
{
image:
'https://pbs.twimg.com/profile_images/1513509255302856718/I9DHE-BG_400x400.jpg',
name: '@rickmanelius',
position: 'CO-Founder',
logo: <Square />,
text: "If you love web3 infra layer tools/solutions, give @laconicnetwork a follow. I'm incredibly excited with what they have coming down the pipeline"
},
{
image:
'https://pbs.twimg.com/profile_images/1467983835627286529/eeb-wHtV_400x400.jpg',
name: '@kumavis',
position: 'CO-Founder',
logo: <Square />,
text: 'Running infura for free is actually extremely expensive, we pursued a light client before but it required a fresh implementation. hopefully @laconicnetwork solves this problem'
},
{
image:
'https://pbs.twimg.com/profile_images/1478264960581091330/I1PG8TSU_400x400.jpg',
name: '@OnChainDreamer',
position: 'CO-Founder',
logo: <Square />,
text: '@laconicnetwork > really smart ppl follows b4 launch'
},
{
image:
'https://pbs.twimg.com/profile_images/1510473579128893446/wkH_3gq3_400x400.jpg',
name: '@GarretteDVF',
position: 'CO-Founder',
logo: <Square />,
text: 'Will also need light clients. Gamer profiles will need some form of p2p gossip on their ranking over time, which makes a good case for composable state information (assuming games are settled on the chain). I would look to @laconicnetwork for some of that.'
},
{
image:
'https://pbs.twimg.com/profile_images/1502059383651549188/w1s1xEnA_400x400.jpg',
name: '@Lifechart3',
position: 'CO-Founder',
logo: <Square />,
text: 'Patiently waiting for @laconicnetwork Launch 😎'
},
{
image:
'https://pbs.twimg.com/profile_images/1513509255302856718/I9DHE-BG_400x400.jpg',
name: '@rickmanelius',
position: 'CO-Founder',
logo: <Square />,
text: "If you love web3 infra layer tools/solutions, give @laconicnetwork a follow. I'm incredibly excited with what they have coming down the pipeline"
},
{
image:
'https://pbs.twimg.com/profile_images/1467983835627286529/eeb-wHtV_400x400.jpg',
name: '@kumavis',
position: 'CO-Founder',
logo: <Square />,
text: 'Running infura for free is actually extremely expensive, we pursued a light client before but it required a fresh implementation. hopefully @laconicnetwork solves this problem'
},
{
image:
'https://pbs.twimg.com/profile_images/1478264960581091330/I1PG8TSU_400x400.jpg',
name: '@OnChainDreamer',
position: 'CO-Founder',
logo: <Square />,
text: '@laconicnetwork > really smart ppl follows b4 launch'
},
{
image:
'https://pbs.twimg.com/profile_images/1510473579128893446/wkH_3gq3_400x400.jpg',
name: '@GarretteDVF',
position: 'CO-Founder',
logo: <Square />,
text: 'Will also need light clients. Gamer profiles will need some form of p2p gossip on their ranking over time, which makes a good case for composable state information (assuming games are settled on the chain). I would look to @laconicnetwork for some of that.'
},
{
image:
'https://pbs.twimg.com/profile_images/1502059383651549188/w1s1xEnA_400x400.jpg',
name: '@Lifechart3',
position: 'CO-Founder',
logo: <Square />,
text: 'Patiently waiting for @laconicnetwork Launch 😎'
}
]

View File

@ -1,31 +1,39 @@
import { useEffect, useState } from 'react'
import { Container } from '~/components/layout/container'
import Section from '~/components/layout/section'
import Heading from '~/components/primitives/heading'
import s from '~/components/sections/terms/termsofuse/termsofuse.module.scss'
import CustomStructuredText from '~/lib/renderer'
import { privacyPolicy } from './privacypolicy'
interface Props {
data: {
privacyContent: any
_updatedAt: string
}
}
const PrivacyPolicy = ({ data }: Props) => {
const [updatedOnDate, setUpdatedOnDate] = useState<string>()
useEffect(() => {
setUpdatedOnDate(
new Date(data?._updatedAt).toLocaleDateString('default', {
day: '2-digit',
month: '2-digit',
year: '2-digit'
})
)
}, [data?._updatedAt])
const PrivacyPolicy = () => {
return (
<Section className={s['section']}>
<div className={s['header']}>
<span>LAST UPDATE</span>
<span className={s['separator']}></span>
<span>{privacyPolicy.updatedOn}</span>
<span>{updatedOnDate}</span>
</div>
<Container className={s['container']}>
<p>{privacyPolicy.text.block01.pg01}</p>
<p>{privacyPolicy.text.block01.pg02}</p>
<p>{privacyPolicy.text.block01.pg03}</p>
<p>{privacyPolicy.text.block01.pg04}</p>
<Heading as="h2" font="tthoves" variant="sm">
{privacyPolicy.text.block02.title}
</Heading>
<p>{privacyPolicy.text.block02.pg01}</p>
<Heading as="h2" font="tthoves" variant="sm">
{privacyPolicy.text.block03.title}
</Heading>
<p>{privacyPolicy.text.block03.pg01}</p>
<CustomStructuredText data={data?.privacyContent} />
</Container>
</Section>
)

View File

@ -1,21 +0,0 @@
export const privacyPolicy = {
updatedOn: '01.31.22',
text: {
block01: {
pg01: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare aliquam pretium. Nullam id massa non augue suscipit porttitor quis quis dolor. Pellentesque porttitor sed turpis vitae pulvinar. Pellentesque a est venenatis, scelerisque lacus ac, vulputate lectus. Etiam porttitor sagittis nisi sit amet lacinia. Cras gravida ultricies porta. Curabitur semper nulla nisi, eget suscipit purus blandit porttitor. Fusce sagittis luctus sapien ut volutpat. Donec luctus, tellus nec gravida ullamcorper, ligula lacus sagittis lacus, ultrices ultricies dolor ligula vitae orci. Quisque a nisi nunc. Maecenas mattis dui id sem porta ultrices.',
pg02: 'Duis quis leo non ex gravida placerat a ac urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus facilisis ipsum ut risus egestas ornare. Phasellus et leo eu purus hendrerit auctor. Nam accumsan enim nec massa semper, vel porttitor neque efficitur. Duis a convallis nulla, a mattis turpis. Mauris a blandit ex. Phasellus sit amet vestibulum purus. Maecenas rutrum pretium lectus ut tincidunt. Donec mollis quam sit amet condimentum lacinia.',
pg03: 'In elementum et felis in elementum. Maecenas nunc nisi, pretium a lobortis interdum, venenatis ut erat. Cras euismod dignissim molestie. Proin commodo nisl at risus gravida facilisis. Donec sit amet magna odio. Pellentesque posuere in risus nec vehicula. Etiam euismod risus eget congue placerat. Pellentesque nec orci ac velit finibus semper a egestas nunc. Integer venenatis, libero sed faucibus tincidunt, massa lorem ullamcorper lacus, quis dignissim libero dolor a nunc. Suspendisse eu accumsan nisi. Integer vitae maximus purus. Nam ut velit a eros tristique feugiat accumsan vitae ex. Pellentesque metus urna, varius non nisl non, molestie sodales diam. Proin sit amet ullamcorper justo. Mauris vehicula dictum imperdiet. Etiam laoreet diam at nunc consectetur sollicitudin.',
pg04: 'Pellentesque ante libero, pellentesque ac sodales et, consectetur a ipsum. Integer aliquam massa et mattis tristique. Praesent id egestas magna. Nam cursus, lectus ac ornare convallis, nisl metus euismod nibh, sed fermentum velit nulla eu neque. Quisque pulvinar enim vel metus aliquam congue. Aenean sit amet ex euismod, ultrices sem quis, dapibus leo. Ut luctus pellentesque orci, fermentum porttitor libero congue eu.'
},
block02: {
title: '1. Lorem Ipsum',
pg01: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
},
block03: {
title: '2. Lorem Ipsum',
pg01: 'Quisque id tristique quam, vitae feugiat orci. Nulla vel nulla et massa fringilla faucibus. Suspendisse semper arcu nec leo maximus fringilla. Praesent id elementum turpis. Cras sit amet nunc sed nunc tempus placerat vel ultrices urna. Aenean scelerisque porta massa, vel sodales turpis porta quis. Aliquam euismod neque ac eros dictum, ac dapibus risus convallis. Mauris risus orci, porttitor et semper eu, accumsan a tellus. Ut posuere venenatis urna sit amet laoreet. Curabitur id justo ut lacus pretium ultrices et in tortor. Phasellus dignissim, justo quis dapibus viverra, nulla justo faucibus sem, quis tristique nulla mi nec purus. Morbi dolor magna, congue ut tempor id, vestibulum et eros. Praesent at quam purus. Nam eget tincidunt tellus, non tristique quam. Vivamus nulla libero, placerat et orci ac.'
}
}
}

View File

@ -41,7 +41,7 @@
line-height: 1.35;
font-size: tovw(30px, 'default', 18px);
width: tovw(635px, 'default', 300px);
width: tovw(585px, 'default', 300px);
margin-top: tovw(62px, 'default', 30px);
}
}
@ -61,7 +61,7 @@
}
top: tovw(-250px);
left: tovw(-400px);
left: tovw(-350px);
width: tovw(1060px, 'default', 300px);
position: absolute;

View File

@ -4,31 +4,36 @@ import Heading from '~/components/primitives/heading'
import s from './app.module.scss'
const AppSection = () => {
interface Props {
data: {
appDescription: string
appHeading: string
appImg: {
url: string
}
appMobileImg: {
url: string
}
}
}
const AppSection = ({ data }: Props) => {
return (
<Section className={s['section']} id="laconicapp">
<Container className={s['container']}>
<div>
<img
className={s.app__img}
src="/images/products/app.png"
alt="token"
/>
<img className={s.app__img} src={data?.appImg?.url} alt="app" />
<img
className={s.app__img__mobile}
src="/images/products/app-mobile.jpg"
alt="token"
src={data?.appMobileImg?.url}
alt="app"
/>
</div>
<div>
<Heading as="h2" variant="lg">
Laconic App
{data?.appHeading}
</Heading>
<p>
Laconic Wallet leverages an in-browser IPFS payment channel,
enabling users of the network to transact quickly and easily with
LNT and ERC20 tokens.
</p>
<p>{data?.appDescription}</p>
</div>
</Container>
</Section>

View File

@ -7,7 +7,23 @@ import Heading from '~/components/primitives/heading'
import s from './hero.module.scss'
const Hero = () => {
interface Props {
data: {
heroHeading: string
heroDescription: string
heroItem01: string
heroItem02: string
heroItem03: string
heroItem04: string
heroItem05: string
heroS01Heading: string
heroS01Desc: string
heroS02Heading: string
heroS02Desc: string
}
}
const Hero = ({ data }: Props) => {
const heroVideoRef = useRef<HTMLVideoElement>(null)
// const loop = () => {
@ -42,43 +58,31 @@ const Hero = () => {
<div className={s.body}>
<div>
<Heading as="h1" variant="xl">
Products
{data?.heroHeading}
</Heading>
<Arrow className={s['arrow']} />
</div>
<div className={s.text__container}>
<p>
Laconic is the only data solution that provides accurate,
low-cost, multi-chain, verifiable blockchain data by leveraging
cryptographic proofs, cutting-edge data-caching engineering, and a
secure verifiable data marketplace:
</p>
<p>{data?.heroDescription}</p>
<div>
<ol>
<li>Laconic Watchers (SDK)</li>
<li>Laconic Stack</li>
<li>Laconic Network</li>
<li>Laconic App</li>
<li>Laconic Network Token (LNT)</li>
<li>{data?.heroItem01}</li>
<li>{data?.heroItem02}</li>
<li>{data?.heroItem03}</li>
<li>{data?.heroItem04}</li>
<li>{data?.heroItem05}</li>
</ol>
<div>
<Heading as="h2" variant="sm">
DeFi
{data?.heroS01Heading}
</Heading>
<p>
Laconic enables simple and efficient DApp development, and the
most flexible views of the Ethereum state.
</p>
<p>{data?.heroS01Desc}</p>
</div>
<div>
<Heading as="h2" variant="sm">
NFTs
{data?.heroS02Heading}
</Heading>
<p>
Laconic exposes cross-chain history and metadata associated
with an NFT, enabling provably accurate data for DApps and
Marketplaces.
</p>
<p>{data?.heroS02Desc}</p>
</div>
</div>
</div>

View File

@ -5,49 +5,54 @@ import Heading from '~/components/primitives/heading'
import s from './network.module.scss'
const Network = () => {
interface Props {
data: {
networkHeading: string
networkDesc: string
networkListItem01: string
networkListItem02: string
networkListItem03: string
networkImg: {
url: string
}
networkMobileImg: {
url: string
}
}
}
const Network = ({ data }: Props) => {
return (
<Section className={s['section']} id="laconicnetwork">
<Container className={s['container']}>
<div>
<Heading as="h2" variant="lg">
Laconic Network
{data?.networkHeading}
</Heading>
<p>
An open, interoperable, verifiable data marketplace and ecosystem of
service providers, DApp operators, and data consumers providing
low-cost, decentralized, and disintermediated horizontal scaling
solutions for projects leveraging Laconic Stack.
</p>
<p>{data?.networkDesc}</p>
<img
className={s.network__img__mobile}
src="/images/products/network-mobile.jpg"
src={data?.networkMobileImg?.url}
alt="network"
/>
<div className={s.features}>
<div>
<img src="/images/products/nw-01.png" alt="" />
<p>
Seamlessly connects DApps and bridges with data service
providers
</p>
<p>{data?.networkListItem01}</p>
</div>
<div>
<img src="/images/products/nw-02.png" alt="" />
<p>Leverages state-channel-based payment infrastructure</p>
<p>{data?.networkListItem02}</p>
</div>
<div>
<img src="/images/products/nw-03.png" alt="" />
<p>
Provides highest quality services at the lowest prices via
marketplace model
</p>
<p>{data?.networkListItem03}</p>
</div>
</div>
</div>
<img
className={s.network__img}
src="/images/products/network.jpg"
src={data?.networkImg?.url}
alt="network"
/>
</Container>

View File

@ -1,30 +1,44 @@
// import { Arrow } from '~/components/icons/arrow'
import Line from '~/components/icons/line'
import { Container } from '~/components/layout/container'
import Section from '~/components/layout/section'
import Heading from '~/components/primitives/heading'
// import Link from '~/components/primitives/link'
import { stackData } from './stack'
import s from './stack.module.scss'
const Stack = () => {
interface Props {
data: {
stackDescription: string
stackHeading: string
stackListData: {
stackData: {
title: string
content: string
}[]
}
stackImage: {
url: string
}
stackSvgImg: {
url: string
}
}
}
const Stack = ({ data }: Props) => {
const stackData = data?.stackListData?.stackData
return (
<Section className={s['section']} id="laconicstack">
<Container className={s['container']}>
<div className={s.header}>
<Heading as="h2" variant="lg">
Laconic Stack
{data?.stackHeading}
</Heading>
<p>
Laconic Stack is a set of essential and verifiable technologies and
services that significantly simplify DApp development and make DApps
accessible to a wider audience of developers.
</p>
<p>{data?.stackDescription}</p>
</div>
<div>
<ol>
{stackData.map((item) => (
{stackData?.map((item) => (
<li key={item.title}>
<div>
<Heading as="h3" variant="sm" font="arthemys">
@ -40,12 +54,12 @@ const Stack = () => {
<div>
<img
className={s.stack__img}
src="/images/products/stack.png"
src={data?.stackImage?.url}
alt="stack"
/>
<img
className={`${s.stack__img} ${s.stack__img__small}`}
src="/images/products/diagram.svg"
src={data?.stackSvgImg?.url}
alt="stack"
/>
<div className={s.gradient} />

View File

@ -5,48 +5,52 @@ import Heading from '~/components/primitives/heading'
import s from './token.module.scss'
const Token = () => {
interface Props {
data: {
tokenHeading: string
tokenDesc: string
tokenItem01: string
tokenItem02: string
tokenItem03: string
tokenImg: {
url: string
}
tokenMobileImg: {
url: string
}
}
}
const Token = ({ data }: Props) => {
return (
<Section className={s['section']} id="laconictoken">
<Container className={s['container']}>
<div>
<Heading as="h2" variant="lg">
Laconic Network Token
{data?.tokenHeading}
</Heading>
<p>
With a fixed total supply, Laconic Network Token (LNT) serves
multiple functions in the Network design, most saliently by securing
incentive alignment across network stakeholders. LNT also allows for
seamless payments across services, regardless of blockchain or DApp.
</p>
<p>{data?.tokenDesc}</p>
<img
className={s.token__img__mobile}
src="/images/products/token-mobile.jpg"
src={data?.tokenMobileImg?.url}
alt="token"
/>
<div className={s.features}>
<div>
<img src="/images/products/tk-01.png" alt="" />
<p>Distribute rewards to staked users</p>
<img src="/images/products/tk-01.png" alt="item" />
<p>{data?.tokenItem01}</p>
</div>
<div>
<img src="/images/products/tk-03.png" alt="" />
<p>Provide service discounts</p>
<img src="/images/products/tk-03.png" alt="item" />
<p>{data?.tokenItem02}</p>
</div>
<div>
<img src="/images/products/tk-04.png" alt="" />
<p>
Distribute funds to community participants for Network
maintenance and development
</p>
<img src="/images/products/tk-04.png" alt="item" />
<p>{data?.tokenItem03}</p>
</div>
</div>
</div>
<img
className={s.token__img}
src="/images/products/token.jpg"
alt="token"
/>
<img className={s.token__img} src={data?.tokenImg?.url} alt="token" />
</Container>
</Section>
)

View File

@ -5,64 +5,64 @@ import Heading from '~/components/primitives/heading'
import s from './watchers.module.scss'
const Watchers = () => {
interface Props {
data: {
watchersHeading: string
watchersImage: {
url: string
}
watchersMobileImage: {
url: string
}
watchersItem01: string
watchersItem02: string
watchersItem03: string
watchersListHead: string
watchersP01: string
watchersP02: string
}
}
const Watchers = ({ data }: Props) => {
return (
<Section className={s['section']} id="laconicwatchers">
<Container className={s['container']}>
<div>
<p>
Developers can leverage the Laconic SDK to develop Watchers, which
are the components of the Laconic Network that makes DApp
development fast and frictionless. Watchers are custom caches of
IPLD blocks. By providing proofs with our IPLD versions of Ethereum
data, a DApp can be fully verifiable with hundreds of megabytes
instead of the tens of terabytes an archive node requires,
dramatically increasing speed and efficiency, and lowering costs.
</p>
<p>
With the services of a Watcher, a DApp developer can exactly define
and customize the API they need. The Watcher runs queries that are
efficient and relevant to the DApp. Another key benefit of Watchers
is that they are composable, enabling a developer to reference data
previously derived from the Ethereum blockchain by means of another
Watcher, within their Watcher.
</p>
<p>A Watcher serves three fundamental purposes:</p>
<p>{data?.watchersP01}</p>
<p>{data?.watchersP02}</p>
<p>{data?.watchersListHead}</p>
<img
className={s.watchers__img__mobile}
src="/images/products/watchers-mobile.jpg"
src={data?.watchersMobileImage?.url}
alt="network"
/>
<div className={s.features}>
<ol>
<li>
<p>It queries precisely the data a DApp needs.</p>
<p>{data?.watchersItem01}</p>
<Line className={s['line']} height={45} />
</li>
<li>
<p>
It transforms the queried data to make it consumable to the
DApp.
</p>
<p>{data?.watchersItem02}</p>
<Line className={s['line']} height={45} />
</li>
<li>
<p>It caches the data for fast and inexpensive access.</p>
<p>{data?.watchersItem03}</p>
<Line className={s['line']} height={45} />
</li>
</ol>
</div>
</div>
<div className={s.heading}>
<Heading as="h2" variant="lg">
Laconic
<br className="hide-on-mobile" /> Watchers
<br />
(SDK)
</Heading>
<Heading
as="h2"
variant="lg"
dangerouslySetInnerHTML={{ __html: data?.watchersHeading }}
/>
<img
className={s.watchers__img}
src="/images/products/watchers.jpg"
src={data?.watchersImage?.url}
alt="network"
/>
</div>

View File

@ -1,31 +1,40 @@
import { useEffect, useState } from 'react'
import { Container } from '~/components/layout/container'
import Section from '~/components/layout/section'
import Heading from '~/components/primitives/heading'
import CustomStructuredText from '~/lib/renderer'
import { termsOfUse } from './termsofuse'
import s from './termsofuse.module.scss'
const TermsOfUse = () => {
interface Props {
data: {
termsContent: any
_updatedAt: string
}
}
const TermsOfUse = ({ data }: Props) => {
const [updatedOnDate, setUpdatedOnDate] = useState<string>()
useEffect(() => {
setUpdatedOnDate(
new Date(data?._updatedAt).toLocaleDateString('default', {
day: '2-digit',
month: '2-digit',
year: '2-digit'
})
)
}, [data?._updatedAt])
return (
<Section className={s['section']}>
<div className={s['header']}>
<span>LAST UPDATE</span>
<span className={s['separator']}></span>
<span>{termsOfUse.updatedOn}</span>
<span>{updatedOnDate}</span>
</div>
<Container className={s['container']}>
<p>{termsOfUse.text.block01.pg01}</p>
<p>{termsOfUse.text.block01.pg02}</p>
<p>{termsOfUse.text.block01.pg03}</p>
<p>{termsOfUse.text.block01.pg04}</p>
<Heading as="h2" font="tthoves" variant="sm">
{termsOfUse.text.block02.title}
</Heading>
<p>{termsOfUse.text.block02.pg01}</p>
<Heading as="h2" font="tthoves" variant="sm">
{termsOfUse.text.block03.title}
</Heading>
<p>{termsOfUse.text.block03.pg01}</p>
<CustomStructuredText data={data?.termsContent} />
</Container>
</Section>
)

View File

@ -1,21 +0,0 @@
export const termsOfUse = {
updatedOn: '01.31.22',
text: {
block01: {
pg01: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ornare aliquam pretium. Nullam id massa non augue suscipit porttitor quis quis dolor. Pellentesque porttitor sed turpis vitae pulvinar. Pellentesque a est venenatis, scelerisque lacus ac, vulputate lectus. Etiam porttitor sagittis nisi sit amet lacinia. Cras gravida ultricies porta. Curabitur semper nulla nisi, eget suscipit purus blandit porttitor. Fusce sagittis luctus sapien ut volutpat. Donec luctus, tellus nec gravida ullamcorper, ligula lacus sagittis lacus, ultrices ultricies dolor ligula vitae orci. Quisque a nisi nunc. Maecenas mattis dui id sem porta ultrices.',
pg02: 'Duis quis leo non ex gravida placerat a ac urna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus facilisis ipsum ut risus egestas ornare. Phasellus et leo eu purus hendrerit auctor. Nam accumsan enim nec massa semper, vel porttitor neque efficitur. Duis a convallis nulla, a mattis turpis. Mauris a blandit ex. Phasellus sit amet vestibulum purus. Maecenas rutrum pretium lectus ut tincidunt. Donec mollis quam sit amet condimentum lacinia.',
pg03: 'In elementum et felis in elementum. Maecenas nunc nisi, pretium a lobortis interdum, venenatis ut erat. Cras euismod dignissim molestie. Proin commodo nisl at risus gravida facilisis. Donec sit amet magna odio. Pellentesque posuere in risus nec vehicula. Etiam euismod risus eget congue placerat. Pellentesque nec orci ac velit finibus semper a egestas nunc. Integer venenatis, libero sed faucibus tincidunt, massa lorem ullamcorper lacus, quis dignissim libero dolor a nunc. Suspendisse eu accumsan nisi. Integer vitae maximus purus. Nam ut velit a eros tristique feugiat accumsan vitae ex. Pellentesque metus urna, varius non nisl non, molestie sodales diam. Proin sit amet ullamcorper justo. Mauris vehicula dictum imperdiet. Etiam laoreet diam at nunc consectetur sollicitudin.',
pg04: 'Pellentesque ante libero, pellentesque ac sodales et, consectetur a ipsum. Integer aliquam massa et mattis tristique. Praesent id egestas magna. Nam cursus, lectus ac ornare convallis, nisl metus euismod nibh, sed fermentum velit nulla eu neque. Quisque pulvinar enim vel metus aliquam congue. Aenean sit amet ex euismod, ultrices sem quis, dapibus leo. Ut luctus pellentesque orci, fermentum porttitor libero congue eu.'
},
block02: {
title: '1. Lorem Ipsum',
pg01: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
},
block03: {
title: '2. Lorem Ipsum',
pg01: 'Quisque id tristique quam, vitae feugiat orci. Nulla vel nulla et massa fringilla faucibus. Suspendisse semper arcu nec leo maximus fringilla. Praesent id elementum turpis. Cras sit amet nunc sed nunc tempus placerat vel ultrices urna. Aenean scelerisque porta massa, vel sodales turpis porta quis. Aliquam euismod neque ac eros dictum, ac dapibus risus convallis. Mauris risus orci, porttitor et semper eu, accumsan a tellus. Ut posuere venenatis urna sit amet laoreet. Curabitur id justo ut lacus pretium ultrices et in tortor. Phasellus dignissim, justo quis dapibus viverra, nulla justo faucibus sem, quis tristique nulla mi nec purus. Morbi dolor magna, congue ut tempor id, vestibulum et eros. Praesent at quam purus. Nam eget tincidunt tellus, non tristique quam. Vivamus nulla libero, placerat et orci ac.'
}
}
}

View File

@ -102,6 +102,10 @@ p {
font-size: tovw(24px, 'default', 18px);
}
.highlight {
color: var(--color-accent);
}
.sr-only {
position: absolute;
border: 0;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
const AboutHero = {
query: `
{
aboutPage {
heroCtaLabel
heroCtaLink
heroDescB01
heroDescB02
heroHeading
heroTitle
}
}`
}
const AboutTeam = {
query: `
{
aboutPage {
teamHeading
teamDescB01
teamDescB02
}
}`
}
const Teams = {
query: `
{
allTeams(orderBy: [id_ASC]) {
id
memberGithub
memberImage {
url
}
memberLinkedin
memberName
memberPosition
memberTeam
memberTwitter
}
}`
}
export { AboutHero, AboutTeam, Teams }

View File

@ -0,0 +1,65 @@
const CommunityHero = {
query: `
{
communityPage {
heroButton01
heroButton02
heroDescription
heroHeading
heroB01Link
heroB02Link
}
}
`
}
const Events = {
query: `
{
allEvents(orderBy: [eventDatetime_ASC]) {
eventDatetime
eventDesc
eventImage {
url
}
eventLink
eventLocation
eventTitle
id
}
}
`
}
const CommunityEvents = {
query: `
{
communityPage {
eventsHeading
eventsDescription
}
}`
}
const CommunitySocials = {
query: `
{
communityPage {
socialsHeading
socialsLine
socialsImage {
url
}
socialsTwitter
socialsDiscord
socialsDiscourse
socialsReddit
socialsTelegram
socialsLinkedin
socialsFacebook
socialsInstagram
}
}`
}
export { CommunityEvents, CommunityHero, CommunitySocials, Events }

View File

@ -0,0 +1,81 @@
const HomeHero = {
query: `
{
homePage {
heroHeading
heroButton01
heroB01Link
heroButton02
heroB02Link
heroSlogan
}
}
`
}
const HomeBenefits = {
query: `
{
homePage {
benefitsDesc
benefitsHeading
benefitsS01Desc
benefitsS01Heading
benefitsS01Item01
benefitsS01Item02
benefitsS01Item03
benefitsS01Item04
benefitsS01Item05
benefitsS01Item06
benefitsS02Desc
benefitsS02Heading
benefitsS02Item01
benefitsS02Item02
benefitsS02Item03
benefitsS02Item04
benefitsS02Item05
benefitsS02Item06
benefitsButton
benefitsButtonLink
}
}
`
}
const HomeOthers = {
query: `
{
homePage {
othersHeading
}
}
`
}
const Testimonials = {
query: `
{
allTestimonials {
id
testimonialImage {
url
}
testimonialPosition
testimonialText
testimonialUsername
}
}
`
}
const HomeLatest = {
query: `
{
homePage {
latestHeading
}
}
`
}
export { HomeBenefits, HomeHero, HomeLatest, HomeOthers, Testimonials }

View File

@ -0,0 +1,27 @@
const Terms = {
query: `
{
termsPage {
termsContent {
value
blocks
}
_updatedAt
}
}`
}
const Privacy = {
query: `
{
privacyPage {
privacyContent {
value
blocks
}
_updatedAt
}
}`
}
export { Privacy, Terms }

View File

@ -0,0 +1,125 @@
const ProductsHero = {
query: `
{
productsPage {
heroDescription
heroHeading
heroItem01
heroItem02
heroItem03
heroItem04
heroItem05
heroS01Desc
heroS01Heading
heroS02Desc
heroS02Heading
}
}
`
}
const ProductsWatchers = {
query: `
{
productsPage {
watchersHeading
watchersImage {
url
}
watchersMobileImage {
url
}
watchersItem01
watchersItem02
watchersItem03
watchersListHead
watchersP01
watchersP02
}
}
`
}
const ProductsStack = {
query: `
{
productsPage {
stackDescription
stackHeading
stackListData
stackImage {
url
}
stackSvgImg {
url
}
}
}
`
}
const ProductsNetwork = {
query: `
{
productsPage {
networkDesc
networkHeading
networkListItem01
networkListItem02
networkListItem03
networkImg {
url
}
networkMobileImg {
url
}
}
}
`
}
const ProductsApp = {
query: `
{
productsPage {
appDescription
appHeading
appImg {
url
}
appMobileImg {
url
}
}
}
`
}
const ProductsToken = {
query: `
{
productsPage {
tokenHeading
tokenDesc
tokenImg {
url
}
tokenItem01
tokenItem02
tokenItem03
tokenMobileImg {
url
}
}
}
`
}
export {
ProductsApp,
ProductsHero,
ProductsNetwork,
ProductsStack,
ProductsToken,
ProductsWatchers
}

33
src/lib/datocms.ts Normal file
View File

@ -0,0 +1,33 @@
import tiny from 'tiny-json-http'
interface props {
query: string
variables?: string
preview?: string
}
export async function request({ query, variables, preview }: props) {
let endpoint = 'https://graphql.datocms.com'
if (preview) {
endpoint += `/preview`
}
const { body } = await tiny.post({
url: endpoint,
headers: {
authorization: `Bearer ${process.env.NEXT_PUBLIC_CMS_ACCESS_TOKEN}`
},
data: {
query,
variables
}
})
if (body.errors) {
console.error('The query has errors!')
throw body.errors
}
return body.data
}

View File

@ -9,6 +9,9 @@ import { PageLayout } from '~/components/layout/page'
import Hero from '~/components/sections/about/hero'
import Related from '~/components/sections/about/related'
import Team from '~/components/sections/about/team'
import { AboutHero, AboutTeam, Teams } from '~/lib/cms/queries/about'
import { request } from '../lib/datocms'
export const getStaticProps = async () => {
const [allBlogPosts, categories] = await Promise.all([
@ -18,12 +21,21 @@ export const getStaticProps = async () => {
const heroBlogPost = allBlogPosts.data[0]
const [heroData, teamsData, teamsPageData] = await Promise.all([
request(AboutHero),
request(Teams),
request(AboutTeam)
])
return {
props: {
initialBlogPosts: {
pagination: allBlogPosts.pagination,
data: allBlogPosts.data.slice(0, 3)
},
heroData: heroData?.aboutPage,
teamsData: teamsData?.allTeams,
teamsPageData: teamsPageData?.aboutPage,
categories,
page:
{
@ -35,7 +47,10 @@ export const getStaticProps = async () => {
}
const About = ({
initialBlogPosts
initialBlogPosts,
heroData,
teamsData,
teamsPageData
}: InferGetStaticPropsType<typeof getStaticProps>) => {
return (
<PageLayout>
@ -45,8 +60,8 @@ const About = ({
{ href: '/videos/banner-about.webm', as: 'fetch' }
]}
/>
<Hero />
<Team />
<Hero data={heroData} />
<Team teamData={teamsData} data={teamsPageData} />
<Related data={initialBlogPosts?.data} />
</PageLayout>
)

View File

@ -1,12 +1,45 @@
import { InferGetStaticPropsType } from 'next'
import { Meta } from '~/components/common/meta'
import { PageLayout } from '~/components/layout/page'
import Events from '~/components/sections/community/events'
import Hero from '~/components/sections/community/hero'
import Socials from '~/components/sections/community/socials'
import {
CommunityEvents,
CommunityHero,
CommunitySocials,
Events as EventsQuery
} from '~/lib/cms/queries/community'
import { Page } from './_app'
import { request } from '../lib/datocms'
const Community: Page = () => {
export const getStaticProps = async () => {
const [heroData, eventsData, eventsSectionData, socialsData] =
await Promise.all([
request(CommunityHero),
request(EventsQuery),
request(CommunityEvents),
request(CommunitySocials)
])
return {
props: {
heroData: heroData?.communityPage,
eventsData: eventsData?.allEvents,
eventsSectionData: eventsSectionData?.communityPage,
socialsData: socialsData?.communityPage
},
revalidate: 60
}
}
const Community = ({
heroData,
eventsData,
eventsSectionData,
socialsData
}: InferGetStaticPropsType<typeof getStaticProps>) => {
return (
<PageLayout>
<Meta
@ -15,9 +48,9 @@ const Community: Page = () => {
{ href: '/videos/banner-community.webm', as: 'fetch' }
]}
/>
<Hero />
<Events />
<Socials />
<Hero data={heroData} />
<Events eventsData={eventsData} data={eventsSectionData} />
<Socials data={socialsData} />
</PageLayout>
)
}

View File

@ -10,6 +10,15 @@ import Benefits from '~/components/sections/homepage/benefits'
import Hero from '~/components/sections/homepage/hero'
import LatestNews from '~/components/sections/homepage/latest-news'
import WhatOthersSay from '~/components/sections/homepage/what-others-say'
import {
HomeBenefits,
HomeHero,
HomeLatest,
HomeOthers,
Testimonials
} from '~/lib/cms/queries/home'
import { request } from '../lib/datocms'
export const getStaticProps = async () => {
const [allBlogPosts, categories] = await Promise.all([
@ -19,12 +28,26 @@ export const getStaticProps = async () => {
const heroBlogPost = allBlogPosts.data[0]
const [heroData, benefitsData, othersData, latestData, testimonialsData] =
await Promise.all([
request(HomeHero),
request(HomeBenefits),
request(HomeOthers),
request(HomeLatest),
request(Testimonials)
])
return {
props: {
initialBlogPosts: {
pagination: allBlogPosts.pagination,
data: allBlogPosts.data.slice(0, 8)
},
heroData: heroData?.homePage,
benefitsData: benefitsData?.homePage,
othersData: othersData?.homePage,
latestData: latestData?.homePage,
testimonialsData: testimonialsData?.allTestimonials,
categories,
page:
{
@ -36,15 +59,20 @@ export const getStaticProps = async () => {
}
const HomePage = ({
initialBlogPosts
initialBlogPosts,
heroData,
benefitsData,
latestData,
othersData,
testimonialsData
}: InferGetStaticPropsType<typeof getStaticProps>) => {
return (
<PageLayout>
<Meta preload={[{ href: '/videos/hero-grid.mp4', as: 'fetch' }]} />
<Hero />
<Benefits />
<LatestNews data={initialBlogPosts?.data} />
<WhatOthersSay />
<Hero data={heroData} />
<Benefits data={benefitsData} />
<LatestNews blogData={initialBlogPosts?.data} data={latestData} />
<WhatOthersSay data={othersData} testimonialsData={testimonialsData} />
</PageLayout>
)
}

View File

@ -1,18 +1,34 @@
import { InferGetStaticPropsType } from 'next'
import { Meta } from '~/components/common/meta'
import { PageLayout } from '~/components/layout/page'
import Hero from '~/components/sections/privacy/hero'
import s from '~/components/sections/privacy/hero/hero.module.scss'
import PrivacyPolicy from '~/components/sections/privacy/privacypolicy'
import { Privacy as PrivacyQuery } from '~/lib/cms/queries/legal'
import { Page } from './_app'
import { request } from '../lib/datocms'
const Privacy: Page = () => {
export const getStaticProps = async () => {
const [privacyData] = await Promise.all([request(PrivacyQuery)])
return {
props: {
privacyData: privacyData?.privacyPage
},
revalidate: 60
}
}
const Privacy = ({
privacyData
}: InferGetStaticPropsType<typeof getStaticProps>) => {
return (
<PageLayout>
<div className={s.gradient}></div>
<Meta />
<Hero />
<PrivacyPolicy />
<PrivacyPolicy data={privacyData} />
</PageLayout>
)
}

View File

@ -13,6 +13,16 @@ import Network from '~/components/sections/products/network'
import Stack from '~/components/sections/products/stack'
import Token from '~/components/sections/products/token'
import Watchers from '~/components/sections/products/watchers'
import {
ProductsApp,
ProductsHero,
ProductsNetwork,
ProductsStack,
ProductsToken,
ProductsWatchers
} from '~/lib/cms/queries/products'
import { request } from '../lib/datocms'
export const getStaticProps = async () => {
const [allBlogPosts, categories] = await Promise.all([
@ -22,12 +32,28 @@ export const getStaticProps = async () => {
const heroBlogPost = allBlogPosts.data[0]
const [heroData, watchersData, stackData, networkData, appData, tokenData] =
await Promise.all([
request(ProductsHero),
request(ProductsWatchers),
request(ProductsStack),
request(ProductsNetwork),
request(ProductsApp),
request(ProductsToken)
])
return {
props: {
initialBlogPosts: {
pagination: allBlogPosts.pagination,
data: allBlogPosts.data.slice(0, 3)
},
heroData: heroData?.productsPage,
watchersData: watchersData?.productsPage,
stackData: stackData?.productsPage,
networkData: networkData?.productsPage,
appData: appData?.productsPage,
tokenData: tokenData?.productsPage,
categories,
page:
{
@ -39,7 +65,13 @@ export const getStaticProps = async () => {
}
const Products = ({
initialBlogPosts
initialBlogPosts,
heroData,
watchersData,
stackData,
networkData,
appData,
tokenData
}: InferGetStaticPropsType<typeof getStaticProps>) => {
return (
<PageLayout>
@ -49,12 +81,12 @@ const Products = ({
{ href: '/videos/banner-products.webm', as: 'fetch' }
]}
/>
<Hero />
<Watchers />
<Stack />
<Network />
<AppSection />
<Token />
<Hero data={heroData} />
<Watchers data={watchersData} />
<Stack data={stackData} />
<Network data={networkData} />
<AppSection data={appData} />
<Token data={tokenData} />
<Related data={initialBlogPosts?.data} />
</PageLayout>
)

View File

@ -1,18 +1,34 @@
import { InferGetStaticPropsType } from 'next'
import { Meta } from '~/components/common/meta'
import { PageLayout } from '~/components/layout/page'
import Hero from '~/components/sections/terms/hero'
import s from '~/components/sections/terms/hero/hero.module.scss'
import TermsOfUse from '~/components/sections/terms/termsofuse'
import { Terms as TermsQuery } from '~/lib/cms/queries/legal'
import { Page } from './_app'
import { request } from '../lib/datocms'
const Terms: Page = () => {
export const getStaticProps = async () => {
const [termsData] = await Promise.all([request(TermsQuery)])
return {
props: {
termsData: termsData?.termsPage
},
revalidate: 60
}
}
const Terms = ({
termsData
}: InferGetStaticPropsType<typeof getStaticProps>) => {
return (
<PageLayout>
<div className={s.gradient}></div>
<Meta />
<Hero />
<TermsOfUse />
<TermsOfUse data={termsData} />
</PageLayout>
)
}

View File

@ -1131,6 +1131,11 @@
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
"@types/tiny-json-http@^7.3.1":
version "7.3.1"
resolved "https://registry.yarnpkg.com/@types/tiny-json-http/-/tiny-json-http-7.3.1.tgz#40339b990b7251689646b985823aa53487dcd77d"
integrity sha512-mJwIwN1W9VLVM7FrGPuWabuEH5FqcOGZByqV4qOSNiwMKtEKym5+6Dyhx2v7vbR/4rSjjUgngmYimaDHQbZQsw==
"@types/websocket@^1.0.4":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.5.tgz#3fb80ed8e07f88e51961211cd3682a3a4a81569c"
@ -2116,6 +2121,15 @@ datocms-structured-text-generic-html-renderer@^2.0.1, datocms-structured-text-ge
dependencies:
datocms-structured-text-utils "^2.0.4"
datocms-structured-text-to-html-string@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/datocms-structured-text-to-html-string/-/datocms-structured-text-to-html-string-2.0.4.tgz#8379040b3af3bbf72c0293ad8a00b30c93bee7b1"
integrity sha512-ulet6wg3cxY4e4Edh0ilMfTfBK8VHeYZwhusx/fJSyk/3A1+PGiQ9EU2j2+OkOxvq+ePo0Iyzi7hgMEutSPRQQ==
dependencies:
datocms-structured-text-generic-html-renderer "^2.0.4"
datocms-structured-text-utils "^2.0.4"
vhtml "^2.2.0"
datocms-structured-text-to-plain-text@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/datocms-structured-text-to-plain-text/-/datocms-structured-text-to-plain-text-2.0.4.tgz#9798c5ea06417e7da4b4b14d1ed3dd9c0d550944"
@ -5832,6 +5846,11 @@ through@^2.3.6, through@^2.3.8:
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
tiny-json-http@^7.4.2:
version "7.4.2"
resolved "https://registry.yarnpkg.com/tiny-json-http/-/tiny-json-http-7.4.2.tgz#2948d7a7f650d69df65f10982f126b5c5520ce3d"
integrity sha512-+3ns4PfQTLaF69zGASkAfDoOEVmwYTXSDrU6VR93h317uFOW7evFzKa7Ih9JzPHiYSee3lUXHLAGhws2wFSexQ==
title-case@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982"
@ -6085,6 +6104,11 @@ value-or-promise@1.0.11, value-or-promise@^1.0.11:
resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140"
integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==
vhtml@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/vhtml/-/vhtml-2.2.0.tgz#369e6823ed6c32cbb9f6e33395bae7c65faa014c"
integrity sha512-TPXrXrxBOslRUVnlVkiAqhoXneiertIg86bdvzionrUYhEuiROvyPZNiiP6GIIJ2Q7oPNVyEtIx8gMAZZE9lCQ==
wcwidth@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"