Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions frontend/src/app/r-and-d/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Hero from "@/components/ResearchDevelopment/Hero";
import TabSection from "@/components/ResearchDevelopment/TabSection";
import { heroQuery, HeroQueryType } from "@/queries/research-development/hero";
import {
tabSectionQuery,
TabSectionQueryType,
} from "@/queries/research-development/tabs-data";
import { request } from "@/utils/graphQLClient";

const ResearchDevelopment: React.FC = async () => {
const heroData = await request<HeroQueryType>(heroQuery);
const tabsData = await request<TabSectionQueryType>(tabSectionQuery);
return (
<>
<Hero {...{ heroData }} />
<TabSection {...{ tabsData }} />
</>
);
};

export default ResearchDevelopment;
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const KlerosScoutSection: React.FC<IKlerosScoutSection> = ({
style={{ backgroundImage: `url(${background.url})` }}
className={clsx(
"relative h-[308px] rounded-2xl md:h-[380px]",
"bg-[#bca2df] bg-cover bg-[-314px] bg-blend-luminosity md:bg-[0px]",
"bg-cover bg-[-314px] md:bg-[0px]",
"flex items-center justify-center pt-3 md:pt-0",
)}
>
Expand Down
34 changes: 34 additions & 0 deletions frontend/src/components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import clsx from "clsx";

interface IPagination {
currentPage: number;
numPages: number;
callback: (newPage: number) => void;
className?: string;
}

const Pagination: React.FC<IPagination> = ({
currentPage,
numPages,
callback,
className,
}) => {
return (
<div className={clsx("flex items-center gap-8", className)}>
{Array.from(Array(numPages), (_, index) => (
<button
key={index}
className={clsx(
"h-4 w-4 rounded-full transition-all",
currentPage === index + 1
? "scale-110 bg-primary-blue"
: "bg-stroke",
)}
onClick={() => callback(index + 1)}
></button>
))}
</div>
);
};

export default Pagination;
60 changes: 60 additions & 0 deletions frontend/src/components/ResearchDevelopment/Hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from "react";

import Image from "next/image";
import Link from "next/link";

import Button from "@/components/Button";
import { HeroQueryType } from "@/queries/research-development/hero";

import ExternalLink from "../ExternalLink";

interface IHero {
heroData: HeroQueryType;
}

const Hero: React.FC<IHero> = ({ heroData }) => {
const { header, subtitle, buttons, arrowLink, background } =
heroData.rAndDPageHero;
return (
<div className="relative px-6 pb-56 pt-44 md:pt-52 lg:px-32 lg:pb-72">
<div className="space-y-8">
<h1 className="pt-1 text-3xl font-medium lg:pt-3 lg:text-4xl">
{header}
</h1>
<p className="max-w-[685px] text-lg">{subtitle}</p>
<div className="flex flex-wrap gap-6">
{buttons.map((button) => (
<Link
key={button.text}
href={button.link.url}
target="_blank"
rel="noopener noreferrer"
>
<Button variant="secondary">
<span>{button.text}</span>
</Button>
</Link>
))}
</div>
<div className="flex flex-wrap gap-8">
{arrowLink.map((link) => (
<ExternalLink
key={link.text}
text={link.text}
url={link.link.url}
className="[&>span]:text-base [&>span]:text-primary-text"
/>
))}
</div>
</div>
<Image
src={background.url}
alt="Hero Image Background"
fill
className="absolute left-0 top-0 z-[-1] h-full object-cover"
/>
</div>
);
};

export default Hero;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Image from "next/image";

import Divider from "@/components/Divider";
import ExternalLink from "@/components/ExternalLink";
import { Fellow } from "@/queries/research-development/tabs-data";

const FellowCard: React.FC<Fellow> = ({
name,
profession,
profilePic,
workText,
reportUrl,
arrowLinkText,
}) => {
return (
<div className="flex flex-col items-start rounded-2xl border border-stroke p-8">
<Image
width={150}
height={150}
src={profilePic.url}
className="mb-6 aspect-square object-contain"
alt="Profile pic"
/>

<h2 className="mb-4 text-lg font-medium text-primary-text">{name}</h2>
<label className="mb-6 font-medium text-secondary-text">
{profession}
</label>
<Divider />

<p className="mb-12 mt-6 text-xl text-primary-text">{workText}</p>
<div className="mt-auto w-full">
<Divider />
<ExternalLink
text={arrowLinkText}
url={reportUrl}
className="mt-6 flex-wrap justify-start"
/>
</div>
</div>
);
};

export default FellowCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useMemo, useState } from "react";

import Pagination from "@/components/Pagination";
import { useScreenSize } from "@/hooks/useScreenSize";
import { Fellow } from "@/queries/research-development/tabs-data";

import FellowCard from "./FellowCard";

const Fellows: React.FC<{ fellows: Fellow[] }> = ({ fellows }) => {
const [page, setPage] = useState(1);

const screenSize = useScreenSize();

const itemsPerPage = useMemo(
() => (screenSize === "sm" ? 1 : 2),
[screenSize],
);

const items = useMemo(
() =>
fellows.slice(
itemsPerPage * (page - 1),
Math.min(fellows.length, itemsPerPage * page),
),
[itemsPerPage, page],
);

return (
<div>
<div className="mb-12 grid grid-cols-1 gap-4 lg:grid-cols-2">
{items.map((fellow) => (
<FellowCard key={fellow.name} {...fellow} />
))}
</div>
<Pagination
currentPage={page}
numPages={Math.ceil(fellows.length / itemsPerPage)}
callback={(val) => setPage(val)}
className="w-full justify-center"
/>
</div>
);
};

export default Fellows;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Image from "next/image";
import Link from "next/link";

import { Testimonial } from "@/queries/research-development/tabs-data";

const TestimonialCard: React.FC<Testimonial> = ({ url, thumbnail }) => (
<Link href={url} target="_blank" rel="noreferrer noopener">
<div className="relative h-[203px] w-full overflow-hidden rounded-2xl">
<Image
src={thumbnail.url}
alt="Thumbnail"
fill
className="object-cover"
/>
</div>
</Link>
);

export default TestimonialCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useMemo, useState } from "react";

import Pagination from "@/components/Pagination";
import { useScreenSize } from "@/hooks/useScreenSize";
import { Testimonial } from "@/queries/research-development/tabs-data";

import TestimonialCard from "./TestimonialCard";

const Testimonials: React.FC<{ testimonials: Testimonial[] }> = ({
testimonials,
}) => {
const [page, setPage] = useState(1);

const screenSize = useScreenSize();

const itemsPerPage = useMemo(
() => (screenSize === "sm" ? 1 : 3),
[screenSize],
);

const items = useMemo(
() =>
testimonials.slice(
itemsPerPage * (page - 1),
Math.min(testimonials.length, itemsPerPage * page),
),
[itemsPerPage, page],
);

return (
<div>
<div className="mb-12 grid grid-cols-1 gap-4 lg:grid-cols-3">
{items.map((testimonial, i) => (
<TestimonialCard key={`${testimonial.url}-${i}`} {...testimonial} />
))}
</div>
<Pagination
currentPage={page}
numPages={Math.ceil(testimonials.length / itemsPerPage)}
callback={(val) => setPage(val)}
className="w-full justify-center"
/>
</div>
);
};

export default Testimonials;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import clsx from "clsx";
import Image from "next/image";
import Link from "next/link";

import Button from "@/components/Button";
import ExternalLink from "@/components/ExternalLink";
import { RAndDPageWaitlistSection } from "@/queries/research-development/tabs-data";

const WaitlistSection: React.FC<RAndDPageWaitlistSection> = ({
header,
applyButton,
arrowLink,
icon,
}) => {
return (
<div
className={clsx(
"bg-background-1",
"px-6 pb-[90px] pt-12 lg:flex-row lg:px-32 lg:py-24",
"flex flex-col items-start justify-start gap-16",
)}
>
<div className="space-y-16">
<h1 className="max-w-[683px] text-2xl font-medium lg:text-3xl">
{header}
</h1>
<div className="flex flex-wrap gap-8">
<Link
href={applyButton.link.url}
target="_blank"
rel="noreferrer noopener"
>
<Button className="text-background-1">{applyButton.text}</Button>
</Link>
<ExternalLink url={arrowLink.link.url} text={arrowLink.text} />
</div>
</div>
<div className="relative h-[267px] w-[295px] flex-shrink-0">
<Image src={icon.url} alt="Icon" fill className="!w-fit" />
</div>
</div>
);
};

export default WaitlistSection;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
Fellow,
RAndDPageFellowshipTabSection,
Testimonial,
} from "@/queries/research-development/tabs-data";

import Fellows from "./Fellows";
import Testimonials from "./Testimonials";

interface IFellowshipTabContent extends RAndDPageFellowshipTabSection {
testimonials: Testimonial[];
fellows: Fellow[];
}

const FellowshipTabContent: React.FC<IFellowshipTabContent> = ({
header,
subtitle,
testimonialsHeader,
fellowsHeader,
testimonials,
fellows,
}) => {
return (
<div className="flex flex-col gap-8 pb-12 pt-[88px] lg:py-24">
<h1 className="text-2xl font-medium text-primary-text md:text-3xl">
{header}
</h1>
<p className="text-lg text-secondary-text">{subtitle}</p>

<h3 className="my-8 text-xl text-secondary-text">{testimonialsHeader}</h3>
<Testimonials {...{ testimonials }} />

<h3 className="my-4 text-xl text-secondary-text">{fellowsHeader}</h3>
<Fellows {...{ fellows }} />
</div>
);
};

export default FellowshipTabContent;
Loading