programs page

This commit is contained in:
Kalidou Diagne
2024-03-13 20:36:36 +00:00
parent 92b6b906b3
commit 8609d00afe
13 changed files with 635 additions and 38 deletions

View File

@@ -1,29 +1,9 @@
import React from "react"
import Image from "next/image"
import { useTranslation } from "@/app/i18n"
import { Accordion } from "@/components/ui/accordion"
import { AppContent } from "@/components/ui/app-content"
import { Label } from "@/components/ui/label"
import { useTranslation } from "@/app/i18n"
const PrincipleImageSizes: Record<string, { width: number; height: number }> = {
"principle-1": {
width: 126,
height: 114,
},
"principle-2": {
width: 176,
height: 260,
},
"principle-3": {
width: 236,
height: 260,
},
"principle-4": {
width: 238,
height: 260,
},
}
export default async function AboutPage({ params: { lang } }: any) {
const { t } = await useTranslation(lang, "about-page")

View File

@@ -0,0 +1,286 @@
"use client"
import { ReactNode } from "react"
import Image from "next/image"
import { accelerationProgramFaq } from "@/data/programs/accelerationProgramFaq"
import { contributionsProgramFaq } from "@/data/programs/contributionsProgramFaq"
import { cn } from "@/lib/utils"
import { Accordion } from "@/components/ui/accordion"
import { AppContent } from "@/components/ui/app-content"
import { Button } from "@/components/ui/button"
import { Label } from "@/components/ui/label"
import { Card } from "@/components/cards/card"
import { Icons } from "@/components/icons"
import { useTranslation } from "@/app/i18n/client"
type ProgramDetailProps = {
region?: string
title: ReactNode
location?: string
date: string
application?: string
size?: "xs" | "sm"
}
const SectionTitle = ({ label }: { label: string }) => {
return (
<span className="text-center font-display text-[32px] font-bold tracking-[3.36px] text-tuatara-950">
{label}
</span>
)
}
const SectionLabel = ({ label }: { label: string }) => {
return (
<span className="text-center font-sans text-base font-bold uppercase tracking-[3.36px] text-tuatara-950">
{label}
</span>
)
}
const ProgramDetail = ({
title,
location,
date,
application,
region,
size = "sm",
}: ProgramDetailProps) => {
return (
<div
className={cn("flex flex-col text-center", {
"gap-10": size === "sm",
"gap-5": size === "xs",
})}
>
<div className="flex flex-col">
<span className="font-display text-lg font-bold leading-none text-black">
{region}
</span>
<span className="font-display text-lg font-bold leading-none text-black">
{title}
</span>
</div>
<div className="flex flex-col gap-6">
{application && (
<div className="mx-auto flex items-center gap-2">
<Icons.checked />
<span className="font-sans text-xs font-normal text-black">
{application}
</span>
</div>
)}
{location && (
<div className="mx-auto flex items-center gap-2">
<Icons.location />
<span className="font-sans text-xs font-normal text-black">
{location}
</span>
</div>
)}
<div className="mx-auto flex items-center gap-2">
<Icons.calendar />
<span className="font-sans text-xs font-normal text-black">
{date}
</span>
</div>
</div>
</div>
)
}
export default function ProgramsPage({ params: { lang } }: any) {
const { t } = useTranslation(lang, "programs-page")
const contributionsProgramDescription: any[] =
t("contributionsProgram.description", {
returnObjects: true,
}) || []
const accelerationProgramDescription: any[] =
t("accelerationProgram.description", {
returnObjects: true,
}) ?? []
const curriculum: any[] =
t("curriculum", {
returnObjects: true,
}) ?? []
return (
<div className="flex flex-col">
<div className="bg-second-gradient">
<AppContent className="flex gap-[56px]">
<div className="grid w-full grid-cols-1 gap-16 py-10 lg:grid-cols-[1fr_1fr] lg:gap-2 lg:py-20">
<div className="flex w-full flex-col gap-8 lg:max-w-[650px]">
<Label.PageTitle label={t("title")} />
<span className="font-sans text-base font-normal leading-[27px] text-tuatara-950">
{t("description")}
</span>
</div>
<Image
width={280}
height={280}
className="mx-auto h-[210px] w-[276px] lg:ml-auto lg:h-[350px] lg:w-[460px]"
src="/images/computer.png"
alt="computer image"
/>
</div>
</AppContent>
</div>
<div className="flex flex-col">
<div className="w-full border-b border-tuatara-300 py-16">
<div className="container mx-auto flex flex-col md:max-w-2xl">
<div className="flex flex-col gap-8">
<SectionTitle label={t("contributionsProgram.title")} />
<div className="flex flex-col">
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<Card className="flex flex-col gap-10">
<ProgramDetail
region="LatAm"
title="Contributions Program"
location="Buenos Aires - Cuenca - San Jose"
date="Jul. 22, 2024 - Sep. 15, 2024"
/>
<Button className="uppercase">
<div className="flex items-center gap-3">
<span>{t("common.applyNow")}</span>
<Icons.arrowRight size={20} />
</div>
</Button>
</Card>
<Card className="flex flex-col gap-10">
<ProgramDetail
region="Asia"
title="Contributions Program"
location="Seoul - Taipei - Tokyo"
date="Jul. 29, 2024 - Sep. 22, 2024"
/>
<Button className="uppercase">
<div className="flex items-center gap-3">
<span>{t("common.applyNow")}</span>
<Icons.arrowRight size={20} />
</div>
</Button>
</Card>
</div>
<div className="flex flex-col gap-2 py-16">
{contributionsProgramDescription?.map(
(description, index) => {
return (
<span
key={index}
className="font-sans text-base text-black"
>
{description}
</span>
)
}
)}
</div>
</div>
<div className="flex flex-col gap-8">
<SectionLabel label={t("common.curriculum")} />
<Card
className="divide-y divide-tuatara-300"
padding="none"
variant="transparent"
>
{curriculum.map(({ title, items }, index) => (
<div
key={index}
className="grid grid-cols-[1fr_2.5fr] divide-x divide-tuatara-300"
>
<div className="flex items-center justify-center bg-anakiwa-100 p-2 text-center">
<span className="text-xs font-bold uppercase tracking-[2.5px] text-tuatara-950">
{t("common.week", {
week: index,
})}
<br />
{title}
</span>
</div>
<div className="px-6 py-2">
<ul className=" list-disc">
{items.map((label: string, index: number) => {
return <li key={index}>{label}</li>
})}
</ul>
</div>
</div>
))}
</Card>
</div>
<div className="flex flex-col gap-8">
<SectionLabel label={t("common.faq")} />
<Accordion
className="!border-anakiwa-300"
size="xs"
items={contributionsProgramFaq.map(
({ question, answer }, index) => {
return {
label: (
<span className="font-sans text-base font-semibold text-black">
{question}
</span>
),
value: `${index}`,
children: answer,
}
}
)}
/>
</div>
</div>
</div>
</div>
<div className="container mx-auto flex flex-col py-16 md:max-w-2xl">
<div className="flex flex-col gap-5">
<SectionTitle label={t("accelerationProgram.title")} />
<Card className="flex flex-col gap-5">
<ProgramDetail
size="xs"
title="Acceleration Program Round 2"
application="Applications Open"
date="Feb. 29, 2024 - May 31, 2024"
/>
<Button className="uppercase">
<div className="flex items-center gap-3">
{t("common.learnMoreOnGithub")}
<Icons.arrowRight size={20} />
</div>
</Button>
</Card>
</div>
<div className="flex flex-col gap-2 py-16">
{accelerationProgramDescription?.map((description, index) => {
return (
<span key={index} className="font-sans text-base text-black">
{description}
</span>
)
})}
</div>
<div className="flex flex-col gap-8">
<SectionLabel label={t("common.faq")} />
<Accordion
className="!border-anakiwa-300"
size="xs"
items={accelerationProgramFaq.map(
({ question, answer }, index) => {
return {
label: (
<span className="font-sans text-base font-semibold text-black">
{question}
</span>
),
value: `${index}`,
children: answer,
}
}
)}
/>
</div>
</div>
</div>
</div>
)
}

View File

@@ -11,7 +11,8 @@
"blog": "Blog",
"activity": "Activity",
"report": "Report",
"firstGoodIssue": "First Good Issue"
"firstGoodIssue": "First Good Issue",
"programs": "Programs"
},
"footer": {
"description": "Privacy + Scaling Explorations is a multidisciplinary team supported by the Ethereum Foundation.",
@@ -70,4 +71,4 @@
"whatDoYouWantDoToday": "What do you want to do today?",
"showingProjects": "Showing {{count}} projects",
"showingProjectsWith": "Showing {{count}} projects with:"
}
}

View File

@@ -0,0 +1,74 @@
{
"title": "Join our programs",
"description": "Want to explore the world of programmable cryptography and learn how to make substantial contributions to open-source projects? Join our free programs to kickstart your journey!",
"common": {
"faq": "FAQ",
"curriculum": "Curriculum",
"howToApply": "How to apply",
"applyNow": "Apply now",
"learnMoreOnGithub": "Learn more on Github",
"week": "Week {{week}}"
},
"curriculum": [
{
"title": "PRE-REQUISITES",
"items": [
"Course overview and resources",
"Git, GitHub, and PR workflow basics",
"Introduction to ZKPs and Number Theory"
]
},
{
"title": "Cryptographic Basics",
"items": [
"Getting started with Circom",
"Basics of encryption and hash functions",
"Digital signatures and elliptic curve cryptography"
]
},
{
"title": "More Crypto + ZKPs",
"items": [
"Circom crash course + practice",
"KZG Commitments and zkSNARKs",
"Overview of Trusted Setups and Groth16"
]
},
{
"title": "Hackathon",
"items": [
"A break from studying",
"One week to build something with your new skills!"
]
},
{
"title": "PLONK Week",
"items": [
"Learn Rust and complete Rustlings",
"Deep dive into PLONK",
"Make a presentation and blog post on PLONK"
]
},
{
"title": "Technologies + Applications",
"items": [
"Halo2 introduction and practical",
"Study of FHE and MPC",
"Explore Semaphore, Bandada, TLSNotary, ZKEmail"
]
}
],
"contributionsProgram": {
"title": "Contributions Program",
"description": [
"The Contributions Program is a hands on entry-level course for university students looking to explore the world of programmable cryptography. This eight-week program will be conducted simultaneously across Argentina (Buenos Aires), Costa Rica (San José), Ecuador (Cuenca), Japan (Tokyo), South Korea (Seoul), and Taiwan (Taipei).",
"By the end of the program, you will gain comprehensive knowledge about Zero Knowledge Proofs (ZKP), Fully Homomorphic Encryption (FHE), and Multiparty Computation (MPC). You will also bolster your GitHub portfolio, and potentially receive grants for further research and contributions!"
]
},
"accelerationProgram": {
"title": "Acceleration Program",
"description": [
"The Acceleration Program supports alumni of our entry level programs (e.g. Contributions Program, ZK Playground) to deepen their explorations of ZKP, FHE, MPC, and related technologies through grants and mentorship. This program operates on a round-based application system, focusing on specific open tasks for each round."
]
}
}

45
components/cards/card.tsx Normal file
View File

@@ -0,0 +1,45 @@
import { VariantProps, cva } from "class-variance-authority"
import { cn } from "@/lib/utils"
const cardVariants = cva("flex flex-col rounded-[8px] overflow-hidden", {
variants: {
variant: {
blue: "bg-anakiwa-100 border border-tuatara-300",
transparent: "bg-transparent border border-tuatara-300",
},
padding: {
none: "p-0",
xs: "p-4",
sm: "py-8 px-6",
},
},
defaultVariants: {
variant: "blue",
padding: "sm",
},
})
interface ProjectCardProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof cardVariants> {}
const Card = ({ variant, padding, children, className }: ProjectCardProps) => {
return (
<div
className={cn(
cardVariants({
variant,
padding,
className,
})
)}
>
{children}
</div>
)
}
Card.displayName = "Card"
export { Card }

View File

@@ -285,8 +285,8 @@ export const Icons = {
),
arrowRight: (props: LucideProps) => (
<svg
width="24"
height="25"
width={props?.size || 24}
height={props?.size || 25}
viewBox="0 0 24 25"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@@ -434,4 +434,78 @@ export const Icons = {
/>
</svg>
),
location: (props: LucideProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="17"
height="16"
viewBox="0 0 17 16"
fill="none"
>
<g clip-path="url(#clip0_7523_20424)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M11.2837 6.87393C11.2837 8.31342 10.1168 9.48029 8.67729 9.48029C7.23786 9.48029 6.07095 8.31342 6.07095 6.87393C6.07095 5.43449 7.23786 4.26758 8.67729 4.26758C10.1168 4.26758 11.2837 5.43449 11.2837 6.87393ZM9.98047 6.87393C9.98047 7.59368 9.39704 8.17711 8.67729 8.17711C7.95761 8.17711 7.37411 7.59368 7.37411 6.87393C7.37411 6.15421 7.95761 5.57076 8.67729 5.57076C9.39704 5.57076 9.98047 6.15421 9.98047 6.87393Z"
fill="#9DABA7"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.45634 10.9436C2.11066 8.71035 2.01952 4.99836 4.25279 2.65269C6.48605 0.307007 10.198 0.215876 12.5437 2.44914C14.8894 4.6824 14.9805 8.39433 12.7473 10.7401L8.70356 14.9873L4.45634 10.9436ZM11.8034 9.84145L8.65834 13.1448L5.35493 9.99979C3.53051 8.26277 3.45963 5.37569 5.19662 3.55128C6.9336 1.72686 9.82065 1.65598 11.6451 3.39297C13.4695 5.12994 13.5404 8.01699 11.8034 9.84145Z"
fill="#9DABA7"
/>
</g>
</svg>
),
calendar: (props: LucideProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="13"
height="13"
viewBox="0 0 13 13"
fill="none"
>
<path
d="M11.2031 1.20312H1.79685C1.53818 1.20312 1.32654 1.41895 1.32654 1.68274V11.2752C1.32654 11.5389 1.53818 11.7548 1.79685 11.7548H11.2031C11.4618 11.7548 11.6734 11.5389 11.6734 11.2752V1.68274C11.6734 1.41895 11.4618 1.20312 11.2031 1.20312Z"
stroke="#9DABA7"
strokeWidth="1.05799"
strokeLinejoin="round"
/>
<path
d="M9.3219 0.245117V2.1636"
stroke="#9DABA7"
strokeWidth="1.05799"
strokeLinejoin="round"
/>
<path
d="M3.67834 0.245117V2.1636"
stroke="#9DABA7"
strokeWidth="1.05799"
strokeLinejoin="round"
/>
<path
d="M1.32654 4.08105H11.6734"
stroke="#9DABA7"
strokeWidth="1.05799"
strokeLinejoin="round"
/>
</svg>
),
checked: (props: LucideProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M1.29691 0.195312C0.73636 0.195312 0.297607 0.657802 0.297607 1.20393V10.7963C0.297607 11.3425 0.73636 11.8049 1.29691 11.8049H10.7031C11.2637 11.8049 11.7025 11.3425 11.7025 10.7963V1.20393C11.7025 0.657783 11.2637 0.195312 10.7031 0.195312H1.29691ZM1.35559 10.747V1.2533H10.6445V10.747H1.35559ZM9.51076 3.01532L4.94684 7.57974L2.48911 5.12201L1.78705 5.82408L4.94684 8.98387L10.2133 3.71739L9.51076 3.01532Z"
fill="#9DABA7"
/>
</svg>
),
}

View File

@@ -1,26 +1,39 @@
"use client"
import React from "react"
import React, { ReactNode } from "react"
import * as RadixAccordion from "@radix-ui/react-accordion"
import { cn } from "@/lib/utils"
import { Icons } from "../icons"
interface AccordionItemProps {
label: string
label: ReactNode
children?: React.ReactNode
value: string
}
type AccordionPadding = "xs" | "sm"
interface AccordionProps extends RadixAccordion.AccordionImplProps {
type: "single" | "multiple"
type?: "single" | "multiple"
size?: AccordionPadding
defaultValue?: string
items: AccordionItemProps[]
className?: string
}
const AccordionSizeMapping: Record<AccordionPadding, string> = {
xs: "pt-4",
sm: "py-6",
}
const Accordion = ({
type = "multiple",
defaultValue,
items,
size = "sm",
className,
}: AccordionProps) => {
return (
<RadixAccordion.Root
@@ -30,21 +43,48 @@ const Accordion = ({
>
{items?.map(({ label, children, value }, accordionIndex) => (
<RadixAccordion.Item
className="group"
className={cn("group", {
"pb-4": size === "xs",
})}
value={value}
key={accordionIndex}
>
<RadixAccordion.Trigger className="flex w-full items-center justify-between border-t border-t-black py-6 ring-0 focus:outline-none">
<span className="block text-left font-sans text-base font-bold uppercase tracking-[3.36px] text-black md:text-xl md:tracking-[4.2px]">
{label}
</span>
<RadixAccordion.Trigger className="w-full">
<div
className={`duration-50 group-hover:visible group-data-[state=open]:hidden md:invisible`}
className={cn(
"flex w-full items-center justify-between border-t border-t-black ring-0 focus:outline-none",
className,
{
[AccordionSizeMapping.xs]: size === "xs",
[AccordionSizeMapping.sm]: size === "sm",
}
)}
>
<Icons.plus className="w-4 md:w-8" />
</div>
<div className={`hidden group-data-[state=open]:block`}>
<Icons.minus className="w-4 md:w-8" />
{typeof label === "string" ? (
<span className="block text-left font-sans text-base font-bold uppercase tracking-[3.36px] text-black md:text-xl md:tracking-[4.2px]">
{label}
</span>
) : (
label
)}
<div
className={`duration-50 group-hover:visible group-data-[state=open]:hidden md:invisible`}
>
<Icons.plus
className={cn({
"w-4 md:w-6": size === "xs",
"w-4 md:w-8": size === "sm",
})}
/>
</div>
<div className={`hidden group-data-[state=open]:block`}>
<Icons.minus
className={cn({
"w-4 md:w-6": size === "xs",
"w-4 md:w-8": size === "sm",
})}
/>
</div>
</div>
</RadixAccordion.Trigger>
<RadixAccordion.Content className="overflow-hidden transition-transform group-data-[state=closed]:animate-slide-up group-data-[state=open]:animate-slide-down">

View File

@@ -0,0 +1,51 @@
import { Faq } from "@/lib/types"
export const accelerationProgramFaq: Faq[] = [
{
question: "Who can apply?",
answer:
"The program is open to alumni of our entry level programs (e.g. Contributions Program, ZK Playground) and other applicants at beginner to intermediate levels with programmable cryptography.",
},
{
question: "What platform does the program use?",
answer:
"We will primarily utilize a Github repository for managing documents and staging of the grant proposals, grantees, and their progress. Stakeholders involved can utilize Github issues and pull requests to comment and collaborate.",
},
{
question: "How does the grant funding work?",
answer: (
<>
<span>
While the core funding this program comes through PSE via the Ethereum
Ecosystem Support Program (ESP), some bounties are supported by
specific teams.
</span>
<span>
Selected grantees will receive a small amount of funding after the
completion of the first milestone. Following milestones will be
awarded larger amounts.
</span>
</>
),
},
{
question: "How many proposals are accepted per open task?",
answer:
"Generally one proposal will be accepted. However, it is possible for applicants to form a team and work collaboratively.",
},
{
question: "How long will I have to tackle the open tasks?",
answer:
"Though our rounds are run in three month periods, grants may last longer depending on task details.",
},
{
question: "Can I propose an open task?",
answer:
"If you have an interesting idea, you can submit it as an self proposed open task. Just to make sure you clearly credit to the original idea and clearly state if that idea is also funded by someone else.",
},
{
question: "What if I have more questions?",
answer:
"For any further questions or additional information, you can join our Telegram group!",
},
]

View File

@@ -0,0 +1,34 @@
import { Faq } from "@/lib/types"
export const contributionsProgramFaq: Faq[] = [
{
question: "Who can apply?",
answer:
"The program is open to university students based in Japan, South Korea, Taiwan, Costa Rica, Ecuador and Argentina with a basic understanding of programming. If you're currently enrolled in a mathematics or computer science program, you're likely an excellent fit.",
},
{
question: "What is the structure of the program?",
answer:
"Our program adopts a hybrid learning model with the majority of learning happening online and weekly in-person meetings for discussions and problem-solving. It consists of three stages: Self-Driven Exploration & Guidance, Hands-On Circuit Writing, and Open-Source Project Contribution.",
},
{
question: "How much time will I need to commit?",
answer:
"We're looking for dedicated students who can commit 40 hours a week from mid-July to September. You will be required to attend an in-person meetup once a week and make presentations.",
},
{
question: "Can I participate remotely?",
answer:
"Unfortunately no, the weekly in-person sessions are required for in-depth discussions and collaborative problem-solving.",
},
{
question: "What will I gain from this program?",
answer:
"Upon completing the program, you'll have comprehensive knowledge about Zero-Knowledge Proofs (ZKP), a bolstered GitHub portfolio from contributing to open-source projects, and potentially earn a grant for further research and contributions.",
},
{
question: "What if I have more questions?",
answer:
"For any further questions or additional information, you can join our Telegram group!",
},
]

View File

@@ -19,6 +19,10 @@ export function useAppSettings(lang: LocaleTypes) {
title: t("menu.projectLibrary"),
href: "/projects",
},
{
title: t("menu.programs"),
href: "/programs",
},
{
title: t("menu.about"),
href: "/about",

View File

@@ -1,7 +1,14 @@
import { ReactNode } from "react"
// list of project groups
export const ProjectSections = ["pse", "grant", "collaboration"] as const
export type ProjectSection = (typeof ProjectSections)[number]
export interface Faq {
question: string
answer: ReactNode
}
export interface AnnounceInterface {
id: number
type?: number

BIN
public/images/computer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

View File

@@ -45,6 +45,7 @@ module.exports = {
tuatara: {
100: "#E5E6E8",
200: "#CDCFD4",
300: "#AAADB6",
500: "#656A75",
600: "#565964",
700: "#4A4C54",