diff --git a/app/[lang]/programs/index.tsx b/app/[lang]/programs/index.tsx new file mode 100644 index 0000000..de9c23f --- /dev/null +++ b/app/[lang]/programs/index.tsx @@ -0,0 +1,612 @@ +"use client" + +import { useCallback, useEffect, useRef, useState } from "react" +import Image from "next/image" +import Link from "next/link" +import { acceleratorProgramFaq } from "@/data/programs/acceleratorProgramFaq" +import { coreProgramFaq } from "@/data/programs/coreProgramFaq" +import { ReactNode } from "react-markdown/lib/ast-to-react" +import { twMerge } from "tailwind-merge" + +import { siteConfig } from "@/config/site" +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 { Dropdown, DropdownProps } from "@/components/ui/dropdown" +import { Card } from "@/components/cards/card" +import { Icons } from "@/components/icons" +import { PageHeader } from "@/components/page-header" +import { useTranslation } from "@/app/i18n/client" + +type ProgramDetailProps = { + region?: string + title: ReactNode + deadline?: string + location?: string + date: string +} + +const SectionTitle = ({ label }: { label: string }) => { + return ( + + {label} + + ) +} + +const AccordionLabel = ({ + label, + className, +}: { + label: string + className?: string +}) => { + return ( + + {label} + + ) +} + +const ProgramDetail = ({ + title, + location, + date, + region, + deadline, +}: ProgramDetailProps) => { + return ( +
+ + {region}
+ {title} +
+ + {deadline && ( + + Application Deadline: {deadline} + + )} + + {location && ( +
+ + + {location} + +
+ )} +
+ + {date} +
+
+ ) +} + +const ProgramSections = ["coreProgram", "acceleratorProgram"] as const + +const ChooseProgramItems: { label: string; value: string; href?: string }[] = [ + { + label: "Core Program", + value: "coreProgram", + href: siteConfig.links.coreProgram, + }, + { + label: "Accelerator Program", + value: "acceleratorProgram", + href: siteConfig.links.acceleratorProgram, + }, +] +export const ProgramPageContent = ({ lang }: any) => { + const { t } = useTranslation(lang, "programs-page") + const { t: common } = useTranslation(lang, "common") + const [activeId, setActiveId] = useState("") + const [isManualScroll, setIsManualScroll] = useState(false) + const [selectedProgram, setSelectedProgram] = useState( + ChooseProgramItems[0].value + ) + const SCROLL_OFFSET = -400 + const sectionsRef = useRef | null>(null) + const [{ value: defaultProgramValue }] = ChooseProgramItems + + const howToApply: any = + t("howToApply", { + returnObjects: true, + }) || [] + + const coreProgramDescription: any[] = + t("coreProgram.description", { + returnObjects: true, + }) || [] + const acceleratorProgramDescription: any[] = + t("acceleratorProgram.description", { + returnObjects: true, + }) ?? [] + + const curriculum: any[] = + t("curriculum", { + returnObjects: true, + }) ?? [] + + useEffect(() => { + if (sectionsRef.current === null) + sectionsRef.current = document.querySelectorAll(`div[data-section]`) + if (!activeId) setActiveId(ProgramSections?.[0] ?? "") + + const handleScroll = () => { + if (isManualScroll) return + + sectionsRef.current?.forEach((section: any) => { + const sectionTop = section.offsetTop - SCROLL_OFFSET + if (window.scrollY >= sectionTop && window.scrollY > 0) { + setActiveId(section.getAttribute("id")) + } + }) + } + + window.addEventListener("scroll", handleScroll) + return () => window.removeEventListener("scroll", handleScroll) + }, [SCROLL_OFFSET, activeId, isManualScroll]) + + const scrollToId = useCallback((id: string) => { + const element = document.getElementById(id) + const top = element?.offsetTop ?? 0 + + if (element) { + setActiveId(id) // active clicked id + setIsManualScroll(true) // tell the window event listener to ignore this scrolling + window?.scrollTo({ + behavior: "smooth", + top: (top ?? 0) - SCROLL_OFFSET, + }) + } + + setTimeout(() => setIsManualScroll(false), 800) + }, []) + + const selectedProgramKey: string = + ChooseProgramItems?.find((item) => item.value === selectedProgram)?.label ?? + "" + const selectedProgramLabel = t(selectedProgramKey) + + const ApplyButton = () => { + return ( + + ) + } + + const selectedProgramUrl = ChooseProgramItems?.find( + (item) => item.value === selectedProgram + )?.href + + return ( +
+
+ {defaultProgramValue && ( + + } + actions={ + defaultProgramValue && ( +
+
+ {common("chooseProgram")}* + setSelectedProgram(value)} + defaultItem={defaultProgramValue} + /> +
+ {!selectedProgram ? ( + + ) : ( + + + + )} +
+ ) + } + /> + )} +
+
+
+ +
+
+
+
+ +
+
+ + + + + + + + + + + + +
+
+ {coreProgramDescription?.map((description, index) => { + return ( + + {description} + + ) + })} +
+
+
+ + ), + value: "curriculum", + children: ( + + {curriculum.map(({ title, items }, index) => ( +
+
+ + {t("common.week", { + week: index, + })} +
+ {title} +
+
+
+
    + {items.map( + (label: string, index: number) => { + return
  • {label}
  • + } + )} +
+
+
+ ))} +
+ ), + }, + ]} + /> + , + value: "faq", + children: ( +
+ { + return { + label: ( + + {question} + + ), + value: index.toString(), + children: ( + + ), + } + } + )} + /> +
+ ), + }, + ]} + /> +
+
+
+
+
+ +
+
+ + +
+
+
+ + + + Acceleration Program
+ Round 2 + + } + deadline="May 31, 2024" + location="Remote Application" + date="June 1, 2024 - August 31, 2024" + /> +
+ + + +
+
+
+
+ {acceleratorProgramDescription?.map((description, index) => { + return ( + + {description} + + ) + })} +
+
+ , + value: "howToApply", + children: ( +
+
+
+
+ + {t("howToApply.openTasks.title")} + +
    + {howToApply?.openTasks?.description?.map( + (task: string, index: number) => { + return ( +
  • +
    +
  • + ) + } + )} +
+
+
+ + {t("howToApply.submitIdea.title")} + +
    + {howToApply?.submitIdea?.description?.map( + (task: string, index: number) => { + return ( +
  • +
    +
  • + ) + } + )} +
+
+ + {t("howToApply.description")} + +
+
+
+ ), + }, + ]} + /> + + , + value: "faq", + children: ( +
+ { + return { + label: ( + + {question} + + ), + value: index.toString(), + children: ( + + {answer} + + ), + } + } + )} + /> +
+ ), + }, + ]} + /> +
+
+
+
+
+
+
+ ) +} diff --git a/app/[lang]/programs/page.tsx b/app/[lang]/programs/page.tsx index e9ac051..9573d77 100644 --- a/app/[lang]/programs/page.tsx +++ b/app/[lang]/programs/page.tsx @@ -1,611 +1,21 @@ -"use client" +import { Metadata } from "next" -import { ReactNode, useCallback, useEffect, useRef, useState } from "react" -import Image from "next/image" -import Link from "next/link" -import { acceleratorProgramFaq } from "@/data/programs/acceleratorProgramFaq" -import { coreProgramFaq } from "@/data/programs/coreProgramFaq" -import { twMerge } from "tailwind-merge" +import { ProgramPageContent } from "." -import { siteConfig } from "@/config/site" -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 { Dropdown, DropdownProps } from "@/components/ui/dropdown" -import { Card } from "@/components/cards/card" -import { Icons } from "@/components/icons" -import { PageHeader } from "@/components/page-header" -import { useTranslation } from "@/app/i18n/client" - -type ProgramDetailProps = { - region?: string - title: ReactNode - deadline?: string - location?: string - date: string -} - -const SectionTitle = ({ label }: { label: string }) => { - return ( - - {label} - - ) -} - -const AccordionLabel = ({ - label, - className, -}: { - label: string - className?: string -}) => { - return ( - - {label} - - ) -} - -const ProgramDetail = ({ - title, - location, - date, - region, - deadline, -}: ProgramDetailProps) => { - return ( -
- - {region}
- {title} -
- - {deadline && ( - - Application Deadline: {deadline} - - )} - - {location && ( -
- - - {location} - -
- )} -
- - {date} -
-
- ) -} - -const ProgramSections = ["coreProgram", "acceleratorProgram"] as const - -const ChooseProgramItems: { label: string; value: string; href?: string }[] = [ - { - label: "Core Program", - value: "coreProgram", - href: siteConfig.links.coreProgram, +export const metadata: Metadata = { + title: "Programs page", + description: "Join our free programs to start your journey!", + openGraph: { + images: [ + { + url: "/programs-page-banner.png", + width: 1200, + height: 630, + }, + ], }, - { - label: "Accelerator Program", - value: "acceleratorProgram", - href: siteConfig.links.acceleratorProgram, - }, -] +} + export default function ProgramsPage({ params: { lang } }: any) { - const { t } = useTranslation(lang, "programs-page") - const { t: common } = useTranslation(lang, "common") - const [activeId, setActiveId] = useState("") - const [isManualScroll, setIsManualScroll] = useState(false) - const [selectedProgram, setSelectedProgram] = useState( - ChooseProgramItems[0].value - ) - const SCROLL_OFFSET = -400 - const sectionsRef = useRef | null>(null) - const [{ value: defaultProgramValue }] = ChooseProgramItems - - const howToApply: any = - t("howToApply", { - returnObjects: true, - }) || [] - - const coreProgramDescription: any[] = - t("coreProgram.description", { - returnObjects: true, - }) || [] - const acceleratorProgramDescription: any[] = - t("acceleratorProgram.description", { - returnObjects: true, - }) ?? [] - - const curriculum: any[] = - t("curriculum", { - returnObjects: true, - }) ?? [] - - useEffect(() => { - if (sectionsRef.current === null) - sectionsRef.current = document.querySelectorAll(`div[data-section]`) - if (!activeId) setActiveId(ProgramSections?.[0] ?? "") - - const handleScroll = () => { - if (isManualScroll) return - - sectionsRef.current?.forEach((section: any) => { - const sectionTop = section.offsetTop - SCROLL_OFFSET - if (window.scrollY >= sectionTop && window.scrollY > 0) { - setActiveId(section.getAttribute("id")) - } - }) - } - - window.addEventListener("scroll", handleScroll) - return () => window.removeEventListener("scroll", handleScroll) - }, [SCROLL_OFFSET, activeId, isManualScroll]) - - const scrollToId = useCallback((id: string) => { - const element = document.getElementById(id) - const top = element?.offsetTop ?? 0 - - if (element) { - setActiveId(id) // active clicked id - setIsManualScroll(true) // tell the window event listener to ignore this scrolling - window?.scrollTo({ - behavior: "smooth", - top: (top ?? 0) - SCROLL_OFFSET, - }) - } - - setTimeout(() => setIsManualScroll(false), 800) - }, []) - - const selectedProgramKey: string = - ChooseProgramItems?.find((item) => item.value === selectedProgram)?.label ?? - "" - const selectedProgramLabel = t(selectedProgramKey) - - const ApplyButton = () => { - return ( - - ) - } - - const selectedProgramUrl = ChooseProgramItems?.find( - (item) => item.value === selectedProgram - )?.href - - return ( -
-
- {defaultProgramValue && ( - - } - actions={ - defaultProgramValue && ( -
-
- {common("chooseProgram")}* - setSelectedProgram(value)} - defaultItem={defaultProgramValue} - /> -
- {!selectedProgram ? ( - - ) : ( - - - - )} -
- ) - } - /> - )} -
-
-
- -
-
-
-
- -
-
- - - - - - - - - - - - -
-
- {coreProgramDescription?.map((description, index) => { - return ( - - {description} - - ) - })} -
-
-
- - ), - value: "curriculum", - children: ( - - {curriculum.map(({ title, items }, index) => ( -
-
- - {t("common.week", { - week: index, - })} -
- {title} -
-
-
-
    - {items.map( - (label: string, index: number) => { - return
  • {label}
  • - } - )} -
-
-
- ))} -
- ), - }, - ]} - /> - , - value: "faq", - children: ( -
- { - return { - label: ( - - {question} - - ), - value: index.toString(), - children: ( - - ), - } - } - )} - /> -
- ), - }, - ]} - /> -
-
-
-
-
- -
-
- - -
-
-
- - - - Acceleration Program
- Round 2 - - } - deadline="May 31, 2024" - location="Remote Application" - date="June 1, 2024 - August 31, 2024" - /> -
- - - -
-
-
-
- {acceleratorProgramDescription?.map((description, index) => { - return ( - - {description} - - ) - })} -
-
- , - value: "howToApply", - children: ( -
-
-
-
- - {t("howToApply.openTasks.title")} - -
    - {howToApply?.openTasks?.description?.map( - (task: string, index: number) => { - return ( -
  • -
    -
  • - ) - } - )} -
-
-
- - {t("howToApply.submitIdea.title")} - -
    - {howToApply?.submitIdea?.description?.map( - (task: string, index: number) => { - return ( -
  • -
    -
  • - ) - } - )} -
-
- - {t("howToApply.description")} - -
-
-
- ), - }, - ]} - /> - - , - value: "faq", - children: ( -
- { - return { - label: ( - - {question} - - ), - value: index.toString(), - children: ( - - {answer} - - ), - } - } - )} - /> -
- ), - }, - ]} - /> -
-
-
-
-
-
-
- ) + return } diff --git a/public/programs-page-banner.png b/public/programs-page-banner.png new file mode 100644 index 0000000..12a38f1 Binary files /dev/null and b/public/programs-page-banner.png differ