diff --git a/app/dev/who-we-are/page.tsx b/app/dev/who-we-are/page.tsx new file mode 100644 index 0000000..b524dee --- /dev/null +++ b/app/dev/who-we-are/page.tsx @@ -0,0 +1,15 @@ +import OurMission from "@/components/OurMission"; +import WhoWeAre from "@/components/WhoWeAre"; + + +export default function WhoWeArePage() { + + return ( + <> +
+ + +
+ + ) +} \ No newline at end of file diff --git a/app/globals.css b/app/globals.css index b5c61c9..f1cd372 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,3 +1,14 @@ +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap"); + @tailwind base; @tailwind components; @tailwind utilities; + +.hide-scrollbar::-webkit-scrollbar { + display: none; +} + +.hide-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 664f909..c424d90 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,8 @@ import Button from "@/components/button"; import Hero from "@/components/hero"; +import OurMission from "@/components/OurMission"; +import WhoWeAre from "@/components/WhoWeAre"; + export default function HomePage() { return ( @@ -18,6 +21,9 @@ export default function HomePage() { } /> + + + ); } diff --git a/components/FeaturedStatistics.tsx b/components/FeaturedStatistics.tsx new file mode 100644 index 0000000..8511ed9 --- /dev/null +++ b/components/FeaturedStatistics.tsx @@ -0,0 +1,65 @@ +/** + * @fileoverview A section that displays a set of large statistics + * and statistic descriptions. + * + * @file FeaturedStatistics.tsx + * @date January 5th, 2023 + * @author Gabriel Sessions + * + */ + +import clsx from "clsx" + +interface FeaturedStatisticsProps { + statistics: Array, + className?: string +} + +/** + * Displays a set of statistics in a row (or column depending on screen size) + * + * @param props - An array of statistics to be fed into DisplayStatistic + */ +export default function FeaturedStatistics(props: FeaturedStatisticsProps) { + return ( +
+ { + props.statistics.map((stat, idx) => +
+ +
+ ) + } +
+ ) +} + +interface Statistic { + statistic: string, + label: string +} + +/** + * Displays a bold statistic (e.g. number) + * and a short description of the statistic + * + * @param props - A statistic and a descriptor (label) + */ +function DisplayStatistic(props: Statistic) { + return ( + <> +
+

{props.statistic}

+

{props.label}

+
+ + ) +} \ No newline at end of file diff --git a/components/FeaturedTextBlock.tsx b/components/FeaturedTextBlock.tsx new file mode 100644 index 0000000..cea70dd --- /dev/null +++ b/components/FeaturedTextBlock.tsx @@ -0,0 +1,31 @@ +/** + * @fileoverview A simple section with a small title and a block of bolded text + * + * @file FeaturedTextBlock.tsx + * @date January 5th, 2025 + * @author Gabriel Sessions + * + */ + +import { ReactNode } from "react" + +interface FeaturedTextBlockProps { + title: ReactNode, + featuredText: ReactNode +} + +/** + * Displays a header and descriptor text below the header. + * + * @param Takes in a title (header) and featured text (descriptor) + */ +export default function FeaturedTextBlock(props: FeaturedTextBlockProps) { + return ( + <> +
+

{props.title}

+

{props.featuredText}

+
+ + ) +} \ No newline at end of file diff --git a/components/ImageCarousel.tsx b/components/ImageCarousel.tsx new file mode 100644 index 0000000..5cb4e8d --- /dev/null +++ b/components/ImageCarousel.tsx @@ -0,0 +1,120 @@ +/** + * @fileoverview A scrollable image carousel which shows a preview of the + * next/previous images. + * + * @file ImageCarousel.tsx + * @date January 5th, 2023 + * @author Gabriel Sessions + * + * @todo Remove hardcoded width/height values if this component is reused + * with different image dimensions. + */ + +"use client" + +import { clsx } from 'clsx'; +import Image from "next/image"; +import { useEffect, useRef, useState } from "react"; + +const IMG_HEIGHT = 389; +const IMG_WIDTH = 665; + +/** + * @param path - Local or absolute path to an image asset + * @param altText - A text descriptor of the image + * @param className - Optional field to add CSS styles + */ +interface ImageProps { + path: string, + altText: string, + className?: string +} + +/** + * @param images - An array of objects to be fed into the Image component + * @param className - Optional field to add CSS styles + */ +interface ImageCarouselProps { + images: Array, + className?: string +} + +/** + * An Image Carousel with basic scrolling functionality + * + * @param props - An array of images + * @todo Adjust the useEffect scrolling logic to display an even number + * of images properly. + */ +export default function ImageCarousel(props: ImageCarouselProps) { + const scrollContainerRef = useRef(null); + + // The carousel is hidden from the user while it loads and waits for the + // useEffect to scroll the carousel + const [isVisible, setIsVisible] = useState(false); + + // Scrolls the carousel to the center when the page loads + useEffect(() => { + if (scrollContainerRef.current) { + const scrollContainer = scrollContainerRef.current; + const centerPosition = (scrollContainer.scrollWidth - scrollContainer.clientWidth) / 2; + scrollContainer.scrollLeft = centerPosition; + setIsVisible(true); + } + }, []); + + return ( +
+
+ {props.images.map((image, idx) => ( + + ))} +
+
+ ) +} + + +/** + * An image container generator. Creates a responsive, rounded box + * for images with a max width of 600px. + * + * @param props - Image path, alt text. Width and height are defined globally. + * @returns A React component + * + * @todo Change width/height to props values if the component needs to be reused + * with new image dimensions. + */ +function ImageContainer(props: ImageProps) { + return ( + {props.altText} + ) +} + diff --git a/components/OurMission.tsx b/components/OurMission.tsx new file mode 100644 index 0000000..ad7f702 --- /dev/null +++ b/components/OurMission.tsx @@ -0,0 +1,26 @@ +/** + * @fileoverview The "Our Mission" section on the JumboCode homepage + * + * @file OurMission.tsx + * @date January 5th, 2025 + * @author Gabriel Sessions + */ + +import FeaturedTextBlock from "./FeaturedTextBlock" + +const ourMissionText = { + title: "Our mission", + featuredText: + <> + To strengthen communities by developing custom software solutions that promote change and foster student growth. + +} + +export default function OurMission() { + + return ( + <> + + + ) +} \ No newline at end of file diff --git a/components/WhoWeAre.tsx b/components/WhoWeAre.tsx new file mode 100644 index 0000000..16f5b8b --- /dev/null +++ b/components/WhoWeAre.tsx @@ -0,0 +1,77 @@ +/** + * @fileoverview The "Who We Are" section of the JumboCode homepage. + * + * @file WhoWeAre.tsx + * @date January 5th, 2025 + * @author Gabriel Sessions + */ + +import FeaturedStatistics from "./FeaturedStatistics"; +import FeaturedTextBlock from "./FeaturedTextBlock"; +import ImageCarousel from "./ImageCarousel"; + +const whoWeAreText = { + title: "Who we are", + featuredText: + <> + JumboCode is a student-run digital agency at Tufts University that provides custom and high-quality software to nonprofits through year-long pro bono projects. + +} + +const whoWeAreImages = [ + { + path: "/homepage/who-we-are-4.png", + altText: "Project presentation" + }, + { + path: "/homepage/who-we-are-1.png", + altText: "A live demo at JumboCode final presentations" + }, + { + path: "/homepage/who-we-are-2.png", + altText: "JumboCode Team Photo" + }, + { + path: "/homepage/who-we-are-3.png", + altText: "A team having fun at a JumboCode hack night" + }, + { + path: "/homepage/who-we-are-5.png", + altText: "JumboHack programmers working on their projects" + } +]; + +const featuredStatistics = [ + { + statistic: "170+", + label: "Annual Members" + }, + { + statistic: "80K+", + label: "Hours Volunteered" + }, + { + statistic: "75+", + label: "Shipped Apps" + } +] + +export default function WhoWeAre() { + return ( + <> +
+ + + + +
+ + ) +} + diff --git a/public/homepage/who-we-are-1.png b/public/homepage/who-we-are-1.png new file mode 100644 index 0000000..d0ce221 Binary files /dev/null and b/public/homepage/who-we-are-1.png differ diff --git a/public/homepage/who-we-are-2.png b/public/homepage/who-we-are-2.png new file mode 100644 index 0000000..d038a9b Binary files /dev/null and b/public/homepage/who-we-are-2.png differ diff --git a/public/homepage/who-we-are-3.png b/public/homepage/who-we-are-3.png new file mode 100644 index 0000000..450cc0d Binary files /dev/null and b/public/homepage/who-we-are-3.png differ diff --git a/public/homepage/who-we-are-4.png b/public/homepage/who-we-are-4.png new file mode 100644 index 0000000..b32e197 Binary files /dev/null and b/public/homepage/who-we-are-4.png differ diff --git a/public/homepage/who-we-are-5.png b/public/homepage/who-we-are-5.png new file mode 100644 index 0000000..89f1035 Binary files /dev/null and b/public/homepage/who-we-are-5.png differ diff --git a/tailwind.config.ts b/tailwind.config.ts index 88ab515..57b8a80 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -10,9 +10,14 @@ const config: Config = { ], theme: { extend: { + fontFamily: { + inter: ['Inter', 'sans-serif'] + }, colors: { gray: colors.zinc, brand: "#32C89E", + background: "#171719", + subtext: "#A1A1A1" }, }, },