Skip to content

Commit 612a600

Browse files
committed
feat(frontend): pnk-token-page
1 parent b6b40b4 commit 612a600

File tree

16 files changed

+483
-3
lines changed

16 files changed

+483
-3
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"
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 ${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/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: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
9+
interface IHero {
10+
heroData: HeroQueryType["pnkTokenPageHero"];
11+
}
12+
13+
const Hero: React.FC<IHero> = ({ heroData }) => {
14+
return (
15+
<div className="relative pt-32 lg:pt-52 pb-56 px-6 lg:px-32">
16+
<div className="space-y-8">
17+
<h1 className="text-3xl lg:text-4xl font-medium">{heroData.header}</h1>
18+
<p className="text-lg">{heroData.subtitle}</p>
19+
<div>
20+
<Link
21+
href={heroData.buyButton.link.url}
22+
target="_blank"
23+
rel="noopener noreferrer"
24+
>
25+
<Button variant="secondary">
26+
<span>{heroData.buyButton.text}</span>
27+
</Button>
28+
</Link>
29+
</div>
30+
<div className="flex flex-wrap gap-6 lg:gap-12 space-x-2">
31+
{heroData.socials.map((social) => (
32+
<Link
33+
key={social.name}
34+
href={social.url}
35+
target="_blank"
36+
rel="noopener noreferrer"
37+
className="block relative h-6 min-w-32"
38+
>
39+
<Image
40+
src={social.icon.url}
41+
fill
42+
alt="social link image"
43+
/>
44+
</Link>
45+
))}
46+
</div>
47+
</div>
48+
<Image
49+
src={heroData.background.url}
50+
alt="Hero Image Background"
51+
fill
52+
className="absolute top-0 left-0 h-full z-[-1] object-cover"
53+
/>
54+
</div>
55+
);
56+
};
57+
58+
export default Hero;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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-4xl 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+
{/* TODO: fix type in PR#28 */}
25+
{/* <div className="w-full items-center">
26+
<ExternalLink
27+
text={tokenNeedData.arrowLink.text}
28+
url={tokenNeedData.arrowLink.link.url}
29+
className="mt-12"
30+
/>
31+
</div> */}
32+
</div>
33+
);
34+
};
35+
36+
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;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import TokenStatDisplay from "./TokenStatDisplay";
2+
3+
import ExternalLink from "@/components/ExternalLink";
4+
import { TokenomicsSectionQueryType } from "@/queries/pnk-token/tokenomics";
5+
6+
interface ITokenomicsSection {
7+
tokenomicsData: TokenomicsSectionQueryType["pnkTokenPageTokenomicsSection"];
8+
}
9+
10+
const TokenomicsSection: React.FC<ITokenomicsSection> = ({
11+
tokenomicsData,
12+
}) => {
13+
return (
14+
<div className="bg-background-2 py-12 lg:py-24 px-6 lg:px-32">
15+
<h1 className="text-3xl text-primary-text font-medium mb-8">
16+
{tokenomicsData.header}
17+
</h1>
18+
<p className="text-lg text-secondary-text mb-16">
19+
{tokenomicsData.subtitle}
20+
</p>
21+
<TokenStatDisplay {...tokenomicsData.tokenStatDisplay} />
22+
<ExternalLink
23+
text={tokenomicsData.arrowLink.text}
24+
url={tokenomicsData.arrowLink.link.url}
25+
className="pt-12 md:pt-16 flex-wrap justify-center md:justify-start"
26+
/>
27+
</div>
28+
);
29+
};
30+
31+
export default TokenomicsSection;

frontend/src/pages/_app.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import "@/styles/globals.css";
33
import { Urbanist } from "next/font/google";
44

55
const urbanist = Urbanist({
6-
weight: "400",
6+
weight: ["400", "500"],
77
subsets: ["latin"]
88
})
99

frontend/src/pages/pnk-token.tsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Footer from "@/components/Footer";
2+
import Navbar from "@/components/Navbar";
3+
import Hero from "@/components/PNKToken/Hero";
4+
import TokenNeedSection from "@/components/PNKToken/TokenNeedSection";
5+
import TokenomicsSection from "@/components/PNKToken/TokenomicsSection";
6+
import { footerQuery, FooterQueryType } from "@/queries/footer";
7+
import { navbarQuery, NavbarQueryType } from "@/queries/navbar";
8+
import { heroQuery, HeroQueryType } from "@/queries/pnk-token/hero";
9+
import {
10+
tokenNeedSectionQuery,
11+
TokenNeedSectionQueryType,
12+
} from "@/queries/pnk-token/token-need";
13+
import {
14+
TokenomicsSectionQueryType,
15+
tokenomicsSectionQuery,
16+
} from "@/queries/pnk-token/tokenomics";
17+
import { graphQLClient } from "@/utils/graphQLClient";
18+
19+
interface IPNKtoken {
20+
navbarData: NavbarQueryType;
21+
footerData: FooterQueryType;
22+
heroData: HeroQueryType["pnkTokenPageHero"];
23+
tokenNeedData: TokenNeedSectionQueryType["pnkTokenPageNeedSection"];
24+
tokenomicsData: TokenomicsSectionQueryType["pnkTokenPageTokenomicsSection"];
25+
}
26+
27+
const PNKToken: React.FC<IPNKtoken> = ({
28+
footerData,
29+
heroData,
30+
tokenNeedData,
31+
tokenomicsData,
32+
navbarData,
33+
}) => {
34+
return (
35+
<div>
36+
<Navbar {...{ navbarData }} />
37+
<Hero {...{ heroData }} />
38+
<TokenNeedSection {...{ tokenNeedData }} />
39+
<TokenomicsSection {...{ tokenomicsData }} />
40+
<Footer {...{ footerData }} />
41+
</div>
42+
);
43+
};
44+
45+
export const getStaticProps = async () => {
46+
const navbarData = await graphQLClient.request<NavbarQueryType>(navbarQuery);
47+
48+
const footerData = await graphQLClient.request<FooterQueryType>(footerQuery);
49+
const heroData = await graphQLClient.request<HeroQueryType>(heroQuery);
50+
const tokenNeedData = await graphQLClient.request<TokenNeedSectionQueryType>(
51+
tokenNeedSectionQuery
52+
);
53+
const tokenomicsData =
54+
await graphQLClient.request<TokenomicsSectionQueryType>(
55+
tokenomicsSectionQuery
56+
);
57+
58+
return {
59+
props: {
60+
navbarData,
61+
footerData,
62+
heroData: heroData.pnkTokenPageHero,
63+
tokenNeedData: tokenNeedData.pnkTokenPageNeedSection,
64+
tokenomicsData: tokenomicsData.pnkTokenPageTokenomicsSection,
65+
},
66+
};
67+
};
68+
69+
export default PNKToken;

0 commit comments

Comments
 (0)