diff --git a/docs/sites/mainweb.md b/docs/sites/mainweb.md index 55575b3d..bc4747fd 100644 --- a/docs/sites/mainweb.md +++ b/docs/sites/mainweb.md @@ -14,7 +14,7 @@ This is the club’s public site **and** the authenticated portal. There is no s | `/` | Home (`HomePageClient`) | | `/team` | Team | | `/events` | Public events | -| `/projects` | Projects | +| `/projects` | Fall 2026 club projects | | `/history` | Club history | | `/bootcamp` | Bootcamp marketing | | `/docs` | In-app docs UI | diff --git a/sites/mainweb/app/HomePageClient.tsx b/sites/mainweb/app/HomePageClient.tsx index 182c0a53..eafbbdca 100644 --- a/sites/mainweb/app/HomePageClient.tsx +++ b/sites/mainweb/app/HomePageClient.tsx @@ -1,6 +1,7 @@ "use client"; import { useState, useEffect, useMemo, useCallback } from "react"; +import type { StaticImageData } from "next/image"; import Image from "next/image"; import Link from "next/link"; @@ -19,9 +20,21 @@ import slide6 from "@/assets/images/slides/slide6.jpg"; import slide7 from "@/assets/images/slides/slide7.jpg"; import slide9 from "@/assets/images/slides/slide9.jpg"; import arc from "@/assets/images/logos/arc-logo-v3.png"; +import dlp from "@/assets/images/logos/dlp4.png"; import gtaa from "@/assets/images/logos/gtaa.png"; import stock from "@/assets/images/logos/stock.png"; -import trading from "@/assets/images/logos/trading.png"; +import { + CURRENT_CLUB_PROJECTS, + PROJECTS_INTEREST_FORM, + projectStatusLabel, +} from "@/lib/club-projects"; + +const PROJECT_LOGOS: Partial> = { + arc, + dlp, + roboinvesting: stock, + sports: gtaa, +}; const Pie = dynamic(() => import("react-chartjs-2").then((mod) => mod.Pie), { ssr: false, @@ -275,151 +288,77 @@ const HomePageClient = () => { Projects.

- Protocol: Member_Initiatives + Fall 2026 club projects

- -
-
- ARC -
-
-

- ARC Research -

-
- - Active - -
-

- ML competition group focusing on Kaggle and TREC research - tracks. -

- - View Club → - -
- - -
-
- Robo -
-
-

- Roboinvesting -

-
- - Active - -
-

- ML-driven trading simulations analyzing technical indicators. -

- - Contact Team → - -
- - -
-
- AI -
-
-

- AI Trading Agent -

-
- - Active - -
-

- Conversational AI tools for real-time portfolio management. -

- - Contact Team → - -
- - -
- Sports -
-

- Sports Analytics -

-
- - Closed - -
-

- NFL projections and NBA roster optimization using advanced - stats. -

-
+ {CURRENT_CLUB_PROJECTS.map((project) => { + const logo = PROJECT_LOGOS[project.slug]; + const statusClass = + project.status === "active" + ? "bg-emerald-500/10 text-emerald-500 border-emerald-500/20" + : "bg-amber-500/10 text-amber-400 border-amber-500/20"; + return ( + + {logo ? ( +
+
+ +
+
+ ) : null} +

+ {project.name} +

+
+ + {projectStatusLabel(project)} + +
+

+ {project.description} +

+ + Details → + +
+ ); + })} -

- Past Archive. + Join a project.

- Explore five years of machine learning projects built by DSGT - members. + One interest form for every Fall 2026 club project.

- Access Database → + Interest form → - +
diff --git a/sites/mainweb/app/projects/page.tsx b/sites/mainweb/app/projects/page.tsx index f2d47991..33d3a129 100644 --- a/sites/mainweb/app/projects/page.tsx +++ b/sites/mainweb/app/projects/page.tsx @@ -1,181 +1,193 @@ -"use client"; - -import { useState, useEffect } from "react"; +import type { Metadata } from "next"; import Link from "next/link"; import Navbar from "@/components/Navbar"; import Footer from "@/components/Footer"; +import type { ClubProject } from "@/lib/club-projects"; +import { + CURRENT_CLUB_PROJECTS, + PAST_CLUB_PROJECTS, + PROJECTS_INTEREST_FORM, + projectStatusLabel, +} from "@/lib/club-projects"; -interface Project { - name: string; - lead: string; - description: string; - tech: string[]; - category: "Deep Learning" | "Finance" | "Sports" | "General DS"; -} +export const metadata: Metadata = { + title: "Projects", + description: + "Current Fall 2026 DS@GT club projects. Join via the interest form.", +}; -const projects: Project[] = [ - { - name: "Deep Learning Playground", - lead: "Noah Iversen", - category: "Deep Learning", - description: - "An interactive web application designed to demystify neural network training. At its core, the project allows users to visualize backpropagation and architecture tweaks in real-time.", - tech: ["AWS", "Docker", "PyTorch", "TypeScript", "NextJs", "Django"], - }, - { - name: "AI-Driven Investment Platform", - lead: "Aryan Hazra", - category: "Finance", - description: - "Using NLP to conversationally help investors reach goals. It adapts strategies based on client information rather than static robo-investing inputs.", - tech: ["NLP", "Machine Learning", "Python", "Data Analytics"], - }, - { - name: "Furnichanter", - lead: "Jane Ivanova", - category: "Deep Learning", - description: - "Seamlessly combining computer vision with interior design. Users can search for furniture via images and generate custom 3D models using text descriptions.", - tech: ["Deep Learning", "3D Modeling", "Python", "Computer Vision"], - }, - { - name: "Kaggle CLEF", - lead: "Anthony Miyaguchi", - category: "General DS", - description: - "A seminar-styled introduction to data science competitions. Members build ML systems for real-world problems like the CLEF 2025 competition.", - tech: [ - "Python", - "Machine Learning", - "Data Science", - "Algorithmic Development", - ], - }, - { - name: "Sports Analysis Project", - lead: "Casper Guo", - category: "Sports", - description: - "Open-ended sports research. Projects include projecting NFL performance, building 'perfect' NBA rosters, and exploiting betting odds differences.", - tech: [ - "Python", - "Machine Learning", - "Data Science", - "Statistical Modeling", - ], - }, -]; +function statusClass(project: ClubProject) { + return project.status === "active" + ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20" + : "bg-amber-500/10 text-amber-400 border-amber-500/20"; +} export default function ProjectsPage() { - const [windowWidth, setWindowWidth] = useState( - typeof window !== "undefined" ? window.innerWidth : 1024, - ); - const categories: Project["category"][] = [ - "Deep Learning", - "Finance", - "Sports", - "General DS", - ]; - - useEffect(() => { - const handleResize = () => setWindowWidth(window.innerWidth); - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); - }, []); - return (
- {/* BREADCRUMB NAVIGATION */} - {/* HERO SECTION */} -
+
+

+ Fall 2026 +

- Project
+ Club
- Archive. + Projects.

- A technical directory of past engineering ventures led by DSGT - members. Organized by domain expertise and technical stack. + Current DS@GT member projects. Several still need a lead. One + interest form covers all of them.

+ + Interest form +
- {/* ORGANIZED CATEGORIES */} -
- {categories.map((cat) => ( -
+ {CURRENT_CLUB_PROJECTS.map((project) => ( +
-
-

- {cat} -

-
-
+
-
- {projects - .filter((p) => p.category === cat) - .map((project, i) => ( -
- {/* Subtle background glow on hover */} -
+
+
+

+ {project.name} +

+ {project.subtitle ? ( +

+ {project.subtitle} +

+ ) : null} +
+ + {projectStatusLabel(project)} + +
-
-

- {project.name} -

- - Lead // {project.lead.split(" ").pop()?.toUpperCase()} - -
+

+ {project.description} +

-

- {project.description} -

+
+
+
Lead
+
+ {project.lead + ? `Lead · ${project.lead}` + : "Needs a lead"} + {project.leadNote ? ` · ${project.leadNote}` : ""} +
+
+ {project.recruiting ? ( +
+
Recruiting
+
Recruiting · {project.recruiting}
+
+ ) : null} +
-
- {project.tech.map((t, index) => ( - - {t} - - ))} -
-
- ))} +
+ {project.links.map((link) => { + const external = /^https?:\/\//.test(link.href); + return ( + + {link.label} → + + ); + })} + + Interest form → +
-
+ ))}
- {/* NAVIGATION FOOTER */} +
+
+

+ Past +

+
+
+

+ Earlier club ventures. These are not the current Fall 2026 roster. +

+
+ {PAST_CLUB_PROJECTS.map((project) => ( +
+
+

+ {project.name} +

+ + {project.lead} + +
+

+ {project.description} +

+
+ ))} +
+
+

- Back to the present? + Want to join one?

+ + Interest_Form{" "} + + → + + {" "} Return_Home - - Join_Bootcamp{" "} - - → - -
diff --git a/sites/mainweb/lib/club-projects.test.ts b/sites/mainweb/lib/club-projects.test.ts new file mode 100644 index 00000000..4d377b69 --- /dev/null +++ b/sites/mainweb/lib/club-projects.test.ts @@ -0,0 +1,138 @@ +import { describe, it, expect } from "vitest"; +import { + CURRENT_CLUB_PROJECTS, + PAST_CLUB_PROJECTS, + PROJECTS_INTEREST_FORM, + projectStatusLabel, +} from "./club-projects"; + +describe("CURRENT_CLUB_PROJECTS", () => { + it("lists the Fall 2026 roster in the published order", () => { + expect(CURRENT_CLUB_PROJECTS.map((p) => p.name)).toEqual([ + "Roboinvesting", + "ARC", + "Deep Learning Playground", + "Sports Analytics", + "DS@GT Website", + "AtlCrime", + ]); + }); + + it("gives every current project a short description", () => { + for (const project of CURRENT_CLUB_PROJECTS) { + expect(project.description.trim().length).toBeGreaterThan(0); + expect(project.description.length).toBeLessThan(160); + } + }); + + it("does not include the removed AI Trading Agent project", () => { + const blob = JSON.stringify(CURRENT_CLUB_PROJECTS); + expect(blob).not.toMatch(/AI Trading Agent/i); + expect(blob).not.toMatch(/Wesley Lu/i); + expect(blob).not.toMatch(/wesleylu@gatech\.edu/i); + }); + + it("does not list Cognitive Science", () => { + const current = JSON.stringify(CURRENT_CLUB_PROJECTS); + const past = JSON.stringify(PAST_CLUB_PROJECTS); + expect(current).not.toMatch(/Cognitive Science/i); + expect(past).not.toMatch(/Cognitive Science/i); + }); + + it("uses the shared interest form", () => { + expect(PROJECTS_INTEREST_FORM).toBe( + "https://forms.gle/Lgoia8m3sAP9XgpB9", + ); + }); + + it("keeps Roboinvesting active with Andrew and a recruiting range", () => { + const project = CURRENT_CLUB_PROJECTS.find( + (p) => p.slug === "roboinvesting", + ); + expect(project?.status).toBe("active"); + expect(project?.lead).toBe("Andrew Hlavacek"); + expect(project?.email).toBe("ahlavacek6@gatech.edu"); + expect(project?.recruiting).toMatch(/4–6/); + expect(project?.links.map((l) => l.href)).toContain( + "https://github.com/DataScience-GT/RoboinvestingDashboard", + ); + }); + + it("points ARC join traffic at dsgt-arc.org/join", () => { + const project = CURRENT_CLUB_PROJECTS.find((p) => p.slug === "arc"); + expect(project?.lead).toBe("Murilo Gustineli"); + expect(project?.links.map((l) => l.href)).toContain( + "https://dsgt-arc.org/join", + ); + }); + + it("does not claim Deep Learning Playground is live", () => { + const project = CURRENT_CLUB_PROJECTS.find((p) => p.slug === "dlp"); + expect(project?.revived).toBe(true); + expect(project?.lead).toBeNull(); + expect(project?.links.map((l) => l.href)).not.toEqual( + expect.arrayContaining([expect.stringMatching(/datasciencegt-dlp/i)]), + ); + expect(JSON.stringify(project)).not.toMatch(/datasciencegt-dlp/i); + expect(project?.links.map((l) => l.href)).toContain( + "https://github.com/DataScience-GT/Deep-Learning-Playground", + ); + }); + + it("points Sports Analytics at AthleticsScrapers, not FA24-Sports-Analysis", () => { + const project = CURRENT_CLUB_PROJECTS.find((p) => p.slug === "sports"); + const hrefs = project?.links.map((l) => l.href).join(" ") ?? ""; + expect(hrefs).toContain( + "https://github.com/DataScience-GT/DSGT-AthleticsScrapers", + ); + expect(hrefs).not.toMatch(/FA24-Sports-Analysis/i); + expect(project?.lead).toBeNull(); + expect(project?.revived).toBe(true); + }); + + it("lists Aamogh as the website lead, limited to 6 people", () => { + const project = CURRENT_CLUB_PROJECTS.find((p) => p.slug === "website"); + expect(project?.status).toBe("active"); + expect(project?.lead).toBe("Aamogh Sawant"); + expect(project?.recruiting).toMatch(/6/); + expect(project?.description).toMatch(/datasciencegt\.org/); + expect(project?.links.map((l) => l.href)).toContain("/"); + expect(project?.links.map((l) => l.href)).toContain( + "https://github.com/DataScience-GT/query", + ); + }); + + it("labels revived projects as needing a lead", () => { + const dlp = CURRENT_CLUB_PROJECTS.find((p) => p.slug === "dlp"); + expect(dlp && projectStatusLabel(dlp)).toBe("Revived · needs a lead"); + }); + + it("lists AtlCrime as needing a lead and points at the org repo", () => { + const project = CURRENT_CLUB_PROJECTS.find((p) => p.slug === "atlcrime"); + expect(project?.status).toBe("needs-lead"); + expect(project?.lead).toBeNull(); + expect(project?.lead).not.toBe("Aamogh Sawant"); + expect(project?.description).toMatch(/heatmap of safety in Atlanta/i); + expect(project?.links.map((l) => l.href)).toContain( + "https://github.com/DataScience-GT/AtlCrime", + ); + }); +}); + +describe("PAST_CLUB_PROJECTS", () => { + it("keeps the old archive names out of the current roster", () => { + const current = CURRENT_CLUB_PROJECTS.map((p) => `${p.name} ${p.lead}`); + expect(current.join(" ")).not.toMatch(/Noah Iversen/); + expect(current.join(" ")).not.toMatch(/Aryan Hazra/); + expect(current.join(" ")).not.toMatch(/Jane Ivanova/); + expect(current.join(" ")).not.toMatch(/Anthony Miyaguchi/); + expect(current.join(" ")).not.toMatch(/Casper Guo/); + expect(PAST_CLUB_PROJECTS.map((p) => p.lead)).toEqual([ + "Noah Iversen", + "Aryan Hazra", + "Jane Ivanova", + "Anthony Miyaguchi", + "Casper Guo", + ]); + }); +}); diff --git a/sites/mainweb/lib/club-projects.ts b/sites/mainweb/lib/club-projects.ts new file mode 100644 index 00000000..cba000c9 --- /dev/null +++ b/sites/mainweb/lib/club-projects.ts @@ -0,0 +1,178 @@ +/** + * Public Fall 2026 club projects. + * + * This is a list, not a table: exec edits it when a project starts, stops, or + * needs a lead. Do not invent live-site claims, extra projects, or stats. + * + * Not listed: AI Trading Agent / Wesley Lu — removed from the current roster. + */ + +export const PROJECTS_INTEREST_FORM = "https://forms.gle/Lgoia8m3sAP9XgpB9"; + +export type ProjectStatus = "active" | "needs-lead"; + +export type ClubProjectLink = { + label: string; + href: string; +}; + +export type ClubProject = { + slug: string; + name: string; + /** Longer name shown under the title when it is not obvious. */ + subtitle?: string; + status: ProjectStatus; + /** True when this is a returning project looking for a new lead. */ + revived?: boolean; + lead: string | null; + leadNote?: string; + email?: string; + recruiting?: string; + description: string; + links: ClubProjectLink[]; +}; + +export const CURRENT_CLUB_PROJECTS: ClubProject[] = [ + { + slug: "roboinvesting", + name: "Roboinvesting", + status: "active", + lead: "Andrew Hlavacek", + email: "ahlavacek6@gatech.edu", + recruiting: "About 4–6 people", + description: + "Student investing research: dashboard, backtesting, and investor personas.", + links: [ + { + label: "GitHub", + href: "https://github.com/DataScience-GT/RoboinvestingDashboard", + }, + { + label: "Email lead", + href: "mailto:ahlavacek6@gatech.edu", + }, + ], + }, + { + slug: "arc", + name: "ARC", + subtitle: "Applied Research Competitions", + status: "active", + lead: "Murilo Gustineli", + description: "Applied research competitions.", + links: [ + { + label: "Join ARC", + href: "https://dsgt-arc.org/join", + }, + ], + }, + { + slug: "dlp", + name: "Deep Learning Playground", + status: "needs-lead", + revived: true, + lead: null, + description: + "Drag-and-drop playground for exploring deep learning models.", + links: [ + { + label: "GitHub", + href: "https://github.com/DataScience-GT/Deep-Learning-Playground", + }, + ], + }, + { + slug: "sports", + name: "Sports Analytics", + status: "needs-lead", + revived: true, + lead: null, + description: "Men's D1 basketball scrapers.", + links: [ + { + label: "GitHub", + href: "https://github.com/DataScience-GT/DSGT-AthleticsScrapers", + }, + ], + }, + { + slug: "website", + name: "DS@GT Website", + status: "active", + lead: "Aamogh Sawant", + recruiting: "Limited to 6 people", + description: + "Public site and member portal, live at datasciencegt.org.", + links: [ + { + label: "Live site", + href: "/", + }, + { + label: "GitHub", + href: "https://github.com/DataScience-GT/query", + }, + ], + }, + { + slug: "atlcrime", + name: "AtlCrime", + status: "needs-lead", + lead: null, + description: "Heatmap of safety in Atlanta.", + links: [ + { + label: "GitHub", + href: "https://github.com/DataScience-GT/AtlCrime", + }, + ], + }, +]; + +export type PastClubProject = { + name: string; + lead: string; + description: string; +}; + +/** Previous ventures. Not the Fall 2026 roster. */ +export const PAST_CLUB_PROJECTS: PastClubProject[] = [ + { + name: "Deep Learning Playground", + lead: "Noah Iversen", + description: + "An interactive web application designed to demystify neural network training. At its core, the project allows users to visualize backpropagation and architecture tweaks in real-time.", + }, + { + name: "AI-Driven Investment Platform", + lead: "Aryan Hazra", + description: + "Using NLP to conversationally help investors reach goals. It adapts strategies based on client information rather than static robo-investing inputs.", + }, + { + name: "Furnichanter", + lead: "Jane Ivanova", + description: + "Seamlessly combining computer vision with interior design. Users can search for furniture via images and generate custom 3D models using text descriptions.", + }, + { + name: "Kaggle CLEF", + lead: "Anthony Miyaguchi", + description: + "A seminar-styled introduction to data science competitions. Members build ML systems for real-world problems like the CLEF 2025 competition.", + }, + { + name: "Sports Analysis Project", + lead: "Casper Guo", + description: + "Open-ended sports research. Projects include projecting NFL performance, building 'perfect' NBA rosters, and exploiting betting odds differences.", + }, +]; + +export function projectStatusLabel(project: ClubProject): string { + if (project.status === "needs-lead") { + return project.revived ? "Revived · needs a lead" : "Needs a lead"; + } + return "Active"; +}