Skip to content

Commit 01428d1

Browse files
authored
Merge pull request #29 from kleros/feat/pnk-token-page
feat(frontend): pnk-token-page
2 parents ac48a40 + 73349e7 commit 01428d1

File tree

23 files changed

+642
-6
lines changed

23 files changed

+642
-6
lines changed

frontend/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
STRAPI_TOKEN=
2+
STRAPI_URL=

frontend/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ yarn-debug.log*
2626
yarn-error.log*
2727

2828
# local env files
29+
.env
2930
.env*.local
3031

3132
# vercel

frontend/src/components/CtaCard.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import Image from "next/image";
2+
3+
import Divider from "./Divider";
4+
import ExternalLink from "./ExternalLink";
5+
6+
export interface ICtaCard {
7+
icon: {
8+
url: string;
9+
};
10+
title: string;
11+
description: string;
12+
arrowLink: {
13+
text: string;
14+
link: {
15+
url: string;
16+
};
17+
};
18+
}
19+
20+
const CtaCard: React.FC<ICtaCard> = ({
21+
icon,
22+
title,
23+
description,
24+
arrowLink,
25+
}) => {
26+
return (
27+
<div className="flex flex-col items-start border border-stroke rounded-2xl p-6">
28+
<Image
29+
width={90}
30+
height={90}
31+
src={icon.url}
32+
className="object-contain mb-4"
33+
alt="Icon"
34+
/>
35+
<h2 className="text-xl text-primary-text font-medium mb-6">{title}</h2>
36+
<p className="text-lg text-secondary-text mb-6">{description}</p>
37+
<Divider />
38+
<ExternalLink
39+
text={arrowLink.text}
40+
url={arrowLink.link.url}
41+
className="mt-6 mb-4"
42+
/>
43+
</div>
44+
);
45+
};
46+
47+
export default CtaCard;

frontend/src/components/Divider.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const Divider: React.FC = () => (
2+
<div className="w-full border border-stroke"></div>
3+
);
4+
export default Divider;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React, { HTMLAttributes } from "react";
2+
3+
import Image from "next/image";
4+
import Link from "next/link";
5+
6+
import LinkArrow from "@/assets/svgs/icons/link-arrow.svg";
7+
8+
interface IExternalLink {
9+
text: string;
10+
url: string;
11+
className?: HTMLAttributes<HTMLDivElement>["className"]
12+
}
13+
const ExternalLink: React.FC<IExternalLink> = ({ text, url, className }) => {
14+
return (
15+
<Link
16+
href={url}
17+
target="_blank"
18+
rel="noopener noreferrer"
19+
className={`flex gap-4 items-center hover:brightness-[1.2] ${className}`}
20+
>
21+
<span className="text-lg text-primary-blue text-center">{text}</span>
22+
<Image
23+
src={LinkArrow}
24+
width="24"
25+
height="24"
26+
alt="Arrow link image"
27+
className="inline"
28+
/>
29+
</Link>
30+
);
31+
};
32+
export default ExternalLink;

frontend/src/components/Navbar/AppsDropdownContent/Card.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import Image from "next/image";
22
import clsx from "clsx";
33
import { Solution } from "@/queries/navbar";
44
import Link from "next/link"
5+
import { hoverEffect } from "@/styles";
6+
57

6-
const hoverEffect = clsx(
7-
"hover:scale-[1.01] transform transition duration-100"
8-
);
98
const cardBaseStyle = clsx(
109
"bg-background-2 rounded-2xl border border-stroke text-wrap",
1110
"p-4 flex gap-4 h-full lg:items-start"

frontend/src/components/Navbar/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ const Navbar: React.FC<INavbar> = ({ navbarData }) => {
5252
isScrolled ? "bg-background-2" : "bg-transparent"
5353
)}
5454
style={{
55-
paddingLeft: responsiveSize(24, 256, 1024, 1920),
56-
paddingRight: responsiveSize(24, 256, 1024, 1920),
55+
paddingLeft: responsiveSize(24, 128, 1024, 1920),
56+
paddingRight: responsiveSize(24, 128, 1024, 1920),
5757
}}
5858
>
5959
<Link href="/" className="flex items-center">
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Exchange } from "@/queries/pnk-token/token-buy";
2+
import { hoverEffect } from "@/styles";
3+
import Image from "next/image";
4+
import Link from "next/link";
5+
6+
interface IExchanges {
7+
exchanges: Exchange[];
8+
}
9+
10+
const Exchanges: React.FC<IExchanges> = ({ exchanges }) => {
11+
return (
12+
<div className="w-full flex flex-wrap justify-center gap-6 md:gap-16 lg:gap-20 mb-8 md:mb-0">
13+
{exchanges.map((exchange) => (
14+
<Link
15+
href={exchange.url}
16+
target="_blank"
17+
rel="noopener noreferrer"
18+
key={exchange.name}
19+
>
20+
<Image
21+
width={64}
22+
height={64}
23+
src={exchange.icon.url}
24+
alt={exchange.name}
25+
className={hoverEffect}
26+
/>
27+
</Link>
28+
))}
29+
</div>
30+
);
31+
};
32+
33+
export default Exchanges;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Image from "next/image";
2+
import Link from "next/link";
3+
4+
import { Exchange } from "@/queries/pnk-token/token-buy";
5+
import clsx from "clsx";
6+
import { hoverEffect } from "@/styles";
7+
8+
interface IFeaturedExchange {
9+
exchange: Exchange;
10+
}
11+
const FeaturedExchange: React.FC<IFeaturedExchange> = ({ exchange }) => {
12+
return (
13+
<Link href={exchange.url} target="_blank" rel="noopener noreferrer">
14+
<div className={clsx("h-40 flex flex-col gap-5 items-center justify-center border border-stroke rounded-2xl px-2 ", hoverEffect)}>
15+
<div className="relative h-16 w-60">
16+
<Image src={exchange.icon.url} alt={exchange.name} fill />
17+
</div>
18+
19+
<p className="text-secondary-text">{exchange.name}</p>
20+
</div>
21+
</Link>
22+
);
23+
};
24+
25+
export default FeaturedExchange;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Exchanges from "./Exchanges";
2+
import FeaturedExchange from "./FeaturedExchange";
3+
4+
import { BuySectionQueryType } from "@/queries/pnk-token/token-buy";
5+
6+
interface IBuySection {
7+
buyData: BuySectionQueryType["pnkTokenPageBuySection"];
8+
}
9+
10+
const BuySection: React.FC<IBuySection> = ({ buyData }) => {
11+
return (
12+
<div className="bg-background-2 py-12 lg:py-24 px-6 lg:px-32">
13+
<h1 className="text-2xl lg:text-3xl text-primary-text font-medium mb-16">
14+
{buyData.header}
15+
</h1>
16+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-12">
17+
{buyData.featuredExchanges.map((exchange) => (
18+
<FeaturedExchange key={exchange.name} {...{ exchange }} />
19+
))}
20+
</div>
21+
<Exchanges exchanges={buyData.exchanges}/>
22+
</div>
23+
);
24+
};
25+
26+
export default BuySection;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { HeroQueryType } from "@/queries/pnk-token/hero";
2+
import Image from "next/image";
3+
import Link from "next/link";
4+
5+
interface IExplorers {
6+
explorers: HeroQueryType["tokenExplorers"];
7+
}
8+
9+
const Explorers: React.FC<IExplorers> = ({ explorers }) => {
10+
return (
11+
<div className="flex flex-wrap gap-6 lg:gap-12">
12+
{explorers.map((explorer) => (
13+
<Link
14+
key={explorer.name}
15+
href={explorer.url}
16+
target="_blank"
17+
rel="noopener noreferrer"
18+
className="block relative h-6 min-w-32 items-start"
19+
>
20+
<Image
21+
src={explorer.icon.url}
22+
fill
23+
alt={explorer.name}
24+
className="!w-min"
25+
/>
26+
</Link>
27+
))}
28+
</div>
29+
);
30+
};
31+
32+
export default Explorers;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from "react";
2+
3+
import Image from "next/image";
4+
import Link from "next/link";
5+
6+
import Button from "@/components/Button";
7+
import { HeroQueryType } from "@/queries/pnk-token/hero";
8+
import Explorers from "./Explorers";
9+
10+
interface IHero {
11+
heroData: HeroQueryType;
12+
}
13+
14+
const Hero: React.FC<IHero> = ({ heroData }) => {
15+
const {header, subtitle, buyButton, background} = heroData.pnkTokenPageHero
16+
return (
17+
<div className="relative pt-52 pb-52 lg:pb-56 px-6 lg:px-32">
18+
<div className="space-y-8">
19+
<h1 className="text-3xl lg:text-4xl font-medium">{header}</h1>
20+
<p className="text-lg">{subtitle}</p>
21+
<div>
22+
<Link
23+
href={buyButton.link.url}
24+
target="_blank"
25+
rel="noopener noreferrer"
26+
>
27+
<Button variant="secondary">
28+
<span>{buyButton.text}</span>
29+
</Button>
30+
</Link>
31+
</div>
32+
<Explorers explorers={heroData.tokenExplorers}/>
33+
</div>
34+
<Image
35+
src={background.url}
36+
alt="Hero Image Background"
37+
fill
38+
className="absolute top-0 left-0 h-full z-[-1] object-cover"
39+
/>
40+
</div>
41+
);
42+
};
43+
44+
export default Hero;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import CtaCard from "@/components/CtaCard";
2+
import ExternalLink from "@/components/ExternalLink";
3+
import { TokenNeedSectionQueryType } from "@/queries/pnk-token/token-need";
4+
5+
interface ITokenNeedSection {
6+
tokenNeedData: TokenNeedSectionQueryType["pnkTokenPageNeedSection"];
7+
}
8+
9+
const TokenNeedSection: React.FC<ITokenNeedSection> = ({ tokenNeedData }) => {
10+
11+
return (
12+
<div className="bg-background-1 py-12 lg:py-24 px-6 lg:px-32">
13+
<h1 className="text-2xl lg:text-3xl text-primary-text font-medium mb-8">
14+
{tokenNeedData.header}
15+
</h1>
16+
<p className="text-lg text-secondary-text mb-16">
17+
{tokenNeedData.subtitle}
18+
</p>
19+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
20+
{tokenNeedData.card.map((card) => (
21+
<CtaCard key={card.title} {...card} />
22+
))}
23+
</div>
24+
<div className="w-full items-center">
25+
<ExternalLink
26+
text={tokenNeedData.arrowLink.text}
27+
url={tokenNeedData.arrowLink.link.url}
28+
className="mt-12 flex-wrap justify-center md:justify-start"
29+
/>
30+
</div>
31+
</div>
32+
);
33+
};
34+
35+
export default TokenNeedSection;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { TokenStat } from "@/queries/pnk-token/tokenomics";
2+
3+
const Stat: React.FC<TokenStat & { label: string }> = ({
4+
label,
5+
primaryValue,
6+
secondaryValue,
7+
}) => {
8+
return (
9+
<div className="flex flex-col">
10+
<label className="text-base text-primary-text">{label}</label>
11+
<div className="flex flex-row gap-4">
12+
<h2 className="text-primary-text font-medium text-xl lg:text-2xl">
13+
{primaryValue}
14+
</h2>
15+
<h2 className="text-primary-blue text-xl lg:text-2xl">
16+
{secondaryValue}
17+
</h2>
18+
</div>
19+
</div>
20+
);
21+
};
22+
23+
export default Stat;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { TokenStatDisplay as ITokenStatDisplay } from "@/queries/pnk-token/tokenomics";
2+
3+
import Image from "next/image";
4+
5+
import Stat from "./Stat";
6+
7+
const TokenStatDisplay: React.FC<ITokenStatDisplay> = ({ icon, stats }) => {
8+
return (
9+
<div className="bg-background-1 md:w-fit flex flex-col gap-8 md:flex-row rounded-2xl md:rounded-full p-6 md:p-4 md:pr-8">
10+
<Image
11+
src={icon.url}
12+
alt="icon"
13+
width={90}
14+
height={90}
15+
className="object-contain"
16+
/>
17+
{stats.map((stat) => (
18+
<Stat
19+
label={stat.key}
20+
primaryValue={stat.primaryValue}
21+
secondaryValue={stat.secondaryValue}
22+
key={stat.key}
23+
/>
24+
))}
25+
</div>
26+
);
27+
};
28+
export default TokenStatDisplay;

0 commit comments

Comments
 (0)