From 45d6c89730bd5fb3bc8a98df62eff0cbf50e2c45 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Thu, 22 Jan 2026 22:21:28 -0500 Subject: [PATCH 01/53] vibe save point --- app/project-directory/page.tsx | 174 +++++++++++- components/project-section.tsx | 47 ++++ components/section-nav.tsx | 131 +++++++++ data/projects.ts | 470 +++++++++++++++++++++------------ types/project.ts | 5 + 5 files changed, 648 insertions(+), 179 deletions(-) create mode 100644 components/project-section.tsx create mode 100644 components/section-nav.tsx diff --git a/app/project-directory/page.tsx b/app/project-directory/page.tsx index ab83b8a..356c381 100644 --- a/app/project-directory/page.tsx +++ b/app/project-directory/page.tsx @@ -1,13 +1,101 @@ "use client" -import { useState } from "react" +import { useState, useMemo, useEffect, useRef } from "react" import Header from "@/components/header" import Footer from "@/components/footer" +import ProjectModal from "@/components/project-modal" +import SectionNav from "@/components/section-nav" +import ProjectSection from "@/components/project-section" +import { projects } from "@/data/projects" +import { Input } from "@/components/ui/input" import type { Project } from "@/types/project" export default function ProjectDirectoryPage() { const [selectedProject, setSelectedProject] = useState(null) const [isModalOpen, setIsModalOpen] = useState(false) + const [searchQuery, setSearchQuery] = useState("") + const [activeSection, setActiveSection] = useState(null) + const [filterSection, setFilterSection] = useState(null) + const observerRef = useRef(null) + const sectionRefs = useRef>(new Map()) + + const filteredProjects = useMemo(() => { + return projects.filter((project) => { + const matchesSearch = searchQuery === "" || project.title.toLowerCase().includes(searchQuery.toLowerCase()) + const matchesSection = filterSection === null || project.sectionName === filterSection + return matchesSearch && matchesSection + }) + }, [searchQuery, filterSection]) + + const sections = useMemo(() => { + const groups = new Map() + filteredProjects.forEach((project) => { + if (!groups.has(project.sectionName)) { + groups.set(project.sectionName, { + projects: [], + type: project.sectionType, + order: project.sectionOrder, + }) + } + groups.get(project.sectionName)!.projects.push(project) + }) + + return Array.from(groups.entries()) + .map(([name, data]) => ({ + name, + count: data.projects.length, + type: data.type, + order: data.order, + projects: data.projects, + })) + .sort((a, b) => { + if (a.type !== b.type) return a.type === "funding" ? -1 : 1 + // For funding sections, lower order = higher priority (Y-Combinator = 1 comes first) + // For cohort sections, higher order = more recent (Winter 2026 = 23 comes first) + return a.type === "funding" ? a.order - b.order : b.order - a.order + }) + }, [filteredProjects]) + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const sectionName = entry.target.getAttribute("data-section-name") + if (sectionName) { + setActiveSection(sectionName) + } + } + }) + }, + { + rootMargin: "-20% 0px -60% 0px", + threshold: 0, + } + ) + + observerRef.current = observer + + return () => { + observer.disconnect() + } + }, []) + + useEffect(() => { + sectionRefs.current.forEach((ref) => { + if (observerRef.current) { + observerRef.current.observe(ref) + } + }) + + return () => { + sectionRefs.current.forEach((ref) => { + if (observerRef.current) { + observerRef.current.unobserve(ref) + } + }) + } + }, [sections]) const openProjectModal = (project: Project) => { setSelectedProject(project) @@ -18,31 +106,91 @@ export default function ProjectDirectoryPage() { setIsModalOpen(false) } + const scrollToSection = (sectionName: string) => { + const element = sectionRefs.current.get(sectionName) + if (element) { + element.scrollIntoView({ behavior: "smooth", block: "start" }) + } + } + + const handleFilterChange = (section: string | null) => { + setFilterSection(section) + } + + const isSectionVisible = (sectionName: string) => { + return filterSection === null || filterSection === sectionName + } + + const getSectionNavData = () => { + const groups = new Map() + projects.forEach((p) => { + groups.set(p.sectionName, (groups.get(p.sectionName) || 0) + 1) + }) + return Array.from(groups.entries()).map(([name, count]) => ({ + name, + count, + type: projects.find((p) => p.sectionName === name)!.sectionType, + })) + } + return (
-

Project Directory

- - {/* Search Bar */} -
-
+
+

Project Directory

+

+ A curated showcase of innovative startups and products built by founders and teams from the V1 ecosystem. +

+
-
- + setSearchQuery(e.target.value)} + className="max-w-md" />
- {/* Project Grid */} -
+ -
+ {sections.length > 0 ? ( + sections.map((section) => { + if (!isSectionVisible(section.name)) return null + return ( + { + if (el) { + sectionRefs.current.set(section.name, el) + } + }} + data-section-name={section.name} + name={section.name} + projects={section.projects} + onProjectClick={openProjectModal} + /> + ) + }) + ) : ( +
+

No projects found matching your criteria.

+

Try adjusting your search or filters.

+
+ )}
+ +
) diff --git a/components/project-section.tsx b/components/project-section.tsx new file mode 100644 index 0000000..d97b7fa --- /dev/null +++ b/components/project-section.tsx @@ -0,0 +1,47 @@ +"use client" + +import { forwardRef } from "react" +import React from "react" +import ProjectCard from "./project-card" +import type { Project } from "@/types/project" +import { Badge } from "@/components/ui/badge" + +interface ProjectSectionProps { + name: string + projects: Project[] + onProjectClick: (project: Project) => void +} + +const ProjectSection = forwardRef>( + ({ name, projects, onProjectClick, ...props }, ref) => { + return ( +
+
+
+

{name} ▪ {projects.length} Project{projects.length !== 1 ? "s" : ""}

+ {/* */} + {/* {projects.length} project{projects.length !== 1 ? "s" : ""} */} + {/* */} +
+
+ +
+ {projects.map((project) => ( + onProjectClick(project)} + /> + ))} +
+
+ ) + } +) + +ProjectSection.displayName = "ProjectSection" + +export default ProjectSection diff --git a/components/section-nav.tsx b/components/section-nav.tsx new file mode 100644 index 0000000..6b4ef20 --- /dev/null +++ b/components/section-nav.tsx @@ -0,0 +1,131 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" + +interface SectionNavProps { + sections: Array<{ + name: string + count: number + type: "funding" | "cohort" + }> + activeSection: string | null + filterSection: string | null + onFilterChange: (section: string | null) => void + onScrollToSection: (section: string) => void +} + +export default function SectionNav({ + sections, + activeSection, + filterSection, + onFilterChange, + onScrollToSection, +}: SectionNavProps) { + const handleSectionClick = (sectionName: string) => { + if (filterSection === sectionName) { + onFilterChange(null) + } else { + onFilterChange(sectionName) + } + } + + const handleAllClick = () => { + onFilterChange(null) + window.scrollTo({ top: 0, behavior: "smooth" }) + } + + // Group sections by type + const fundingSections = sections.filter(s => s.type === 'funding') + const cohortSections = sections.filter(s => s.type === 'cohort') + + // Create shorter display names for cohorts + const getDisplayName = (sectionName: string, type: string) => { + if (type === 'cohort') { + // Convert "Winter 2026 Product Studio Cohort" to "W26 Cohort" + const match = sectionName.match(/(\w+)\s+(\d{4})\s+Product Studio Cohort/) + if (match) { + const [_, season, year] = match + const seasonShort = season === 'Winter' ? 'W' : season === 'Fall' ? 'F' : season + const yearShort = year.slice(-2) // Get last 2 digits + return `${seasonShort}${yearShort} Cohort` + } + } + return sectionName + } + + return ( +
+
+
+ + + {/* Funding sections */} + {fundingSections.map((section) => ( + + ))} + + {/* Separator */} + {cohortSections.length > 0 && ( +
+ )} + + {/* Cohort sections */} + {cohortSections.map((section) => ( + + ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/data/projects.ts b/data/projects.ts index 1250dcd..053954f 100644 --- a/data/projects.ts +++ b/data/projects.ts @@ -3,314 +3,452 @@ import type { Project } from "@/types/project" export const projects: Project[] = [ { id: "project-1", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + title: "TechFlow AI", + description: "AI-powered workflow automation for enterprises", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 1", "Category 2", "Category 3"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "TechFlow Inc.", + companyWebsite: "https://techflow.ai", + categories: ["AI/ML", "Enterprise", "B2B"], + overview: "TechFlow AI helps enterprises automate complex workflows using artificial intelligence. Our platform integrates with existing tools and learns from your business processes to provide intelligent automation suggestions.", founders: [ { id: "founder-1", - name: "John Doe", - role: "SWE", + name: "Alex Chen", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { id: "founder-2", - name: "John Doe", - role: "SWE", + name: "Sarah Kim", + role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", }, + ], + sectionType: "funding", + sectionName: "Y-Combinator", + sectionOrder: 1, + }, + { + id: "project-2", + title: "HealthSync", + description: "Personal health monitoring and insights platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "HealthSync Labs", + companyWebsite: "https://healthsync.com", + categories: ["Healthcare", "Consumer", "B2C"], + overview: "HealthSync provides personalized health insights by integrating with wearable devices and medical records. Our AI-powered platform helps users understand their health trends and make informed decisions.", + founders: [ { id: "founder-3", - name: "John Doe", - role: "SWE", + name: "Jordan Lee", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { id: "founder-4", - name: "John Doe", - role: "SWE", + name: "Maria Rodriguez", + role: "CPO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "funding", + sectionName: "Y-Combinator", + sectionOrder: 1, }, { - id: "project-2", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-3", + title: "CloudScale", + description: "Multi-cloud cost optimization and management", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 1", "Category 3"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "CloudScale Inc.", + companyWebsite: "https://cloudscale.io", + categories: ["Cloud", "Enterprise", "B2B"], + overview: "CloudScale helps companies optimize their cloud spending across multiple providers. Our platform provides real-time cost tracking, optimization recommendations, and automated resource scaling.", founders: [ { id: "founder-5", - name: "John Doe", - role: "SWE", + name: "David Park", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { id: "founder-6", - name: "John Doe", - role: "SWE", + name: "Emily Watson", + role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "funding", + sectionName: "Other Funding", + sectionOrder: 2, }, { - id: "project-3", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-4", + title: "EduConnect", + description: "Learning management for K-12 classrooms", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 2"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "EduConnect Systems", + companyWebsite: "https://educonnect.com", + categories: ["EdTech", "Education", "B2B"], + overview: "EduConnect provides a comprehensive learning management system designed for K-12 classrooms. Our platform simplifies assignment distribution, grading, and parent communication.", founders: [ { id: "founder-7", - name: "John Doe", - role: "SWE", + name: "Lisa Chang", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, + ], + sectionType: "funding", + sectionName: "Other Funding", + sectionOrder: 2, + }, + { + id: "project-5", + title: "GreenRoute", + description: "Sustainable logistics and delivery optimization", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "GreenRoute Logistics", + companyWebsite: "https://greenroute.io", + categories: ["Logistics", "Sustainability", "B2B"], + overview: "GreenRoute helps delivery companies reduce their carbon footprint through intelligent route optimization. Our AI algorithms consider traffic, weather, and environmental factors to minimize emissions.", + founders: [ { id: "founder-8", - name: "John Doe", - role: "SWE", + name: "Michael Brown", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { id: "founder-9", - name: "John Doe", - role: "SWE", + name: "Anna Green", + role: "COO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, }, { - id: "project-4", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-6", + title: "FinanceHub", + description: "Personal finance tracking for college students", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 1"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "FinanceHub Inc.", + companyWebsite: "https://financehub.com", + categories: ["FinTech", "Consumer", "B2C"], + overview: "FinanceHub helps college students manage their finances with features tailored to student life. Track expenses, budget for tuition, and learn financial literacy in an easy-to-use app.", founders: [ { id: "founder-10", - name: "John Doe", - role: "SWE", + name: "Rachel Torres", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-11", + name: "James Wilson", + role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, }, { - id: "project-5", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-7", + title: "EventSpace", + description: "Campus event discovery and ticketing platform", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 2", "Category 3"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "EventSpace Inc.", + companyWebsite: "https://eventspace.com", + categories: ["Events", "Social", "B2C"], + overview: "EventSpace makes it easy to discover and attend campus events. Students can find events, buy tickets, and coordinate with friends. Event organizers get tools to manage and promote their events.", founders: [ - { - id: "founder-11", - name: "John Doe", - role: "SWE", - imageSrc: "/placeholder.svg?height=150&width=150", - }, { id: "founder-12", - name: "John Doe", - role: "SWE", + name: "Kevin Patel", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, }, { - id: "project-6", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-8", + title: "StudyBuddy", + description: "AI-powered study partner and flashcard generator", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 1", "Category 2"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "StudyBuddy AI", + companyWebsite: "https://studybuddy.ai", + categories: ["EdTech", "AI/ML", "B2C"], + overview: "StudyBuddy uses AI to generate flashcards from your notes, quiz you on key concepts, and track your learning progress. It's like having a personalized tutor available 24/7.", founders: [ { id: "founder-13", - name: "John Doe", - role: "SWE", + name: "Nina Williams", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { id: "founder-14", - name: "John Doe", - role: "SWE", + name: "Tom Anderson", + role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, }, { - id: "project-7", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-9", + title: "MentorMatch", + description: "Platform connecting students with industry mentors", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 3"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "MentorMatch Inc.", + companyWebsite: "https://mentormatch.com", + categories: ["Career", "Networking", "B2B"], + overview: "MentorMatch connects students with industry professionals for mentorship. Students can search for mentors in their field, schedule sessions, and build lasting professional relationships.", founders: [ { id: "founder-15", - name: "John Doe", - role: "SWE", + name: "Sofia Martinez", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { id: "founder-16", - name: "John Doe", - role: "SWE", + name: "Daniel Kim", + role: "COO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Winter 2025 Product Studio Cohort", + sectionOrder: 22, }, { - id: "project-8", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-10", + title: "GreenRoute", + description: "Sustainable logistics and delivery optimization", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 1", "Category 3"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "GreenRoute Logistics", + companyWebsite: "https://greenroute.io", + categories: ["Logistics", "Sustainability", "B2B"], + overview: "GreenRoute helps delivery companies reduce their carbon footprint through intelligent route optimization. Our AI algorithms consider traffic, weather, and environmental factors to minimize emissions.", founders: [ { - id: "founder-17", - name: "John Doe", - role: "SWE", + id: "founder-8", + name: "Michael Brown", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-18", - name: "John Doe", - role: "SWE", + id: "founder-9", + name: "Anna Green", + role: "COO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, }, { - id: "project-9", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-11", + title: "EduConnect", + description: "Learning management for K-12 classrooms", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 2"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "EduConnect Systems", + companyWebsite: "https://educonnect.com", + categories: ["EdTech", "Education", "B2B"], + overview: "EduConnect provides a comprehensive learning management system designed for K-12 classrooms. Our platform simplifies assignment distribution, grading, and parent communication.", founders: [ { - id: "founder-19", - name: "John Doe", - role: "SWE", + id: "founder-7", + name: "Lisa Chang", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, }, { - id: "project-10", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-12", + title: "CloudScale", + description: "Multi-cloud cost optimization and management", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 1"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "CloudScale Inc.", + companyWebsite: "https://cloudscale.io", + categories: ["Cloud", "Enterprise", "B2B"], + overview: "CloudScale helps companies optimize their cloud spending across multiple providers. Our platform provides real-time cost tracking, optimization recommendations, and automated resource scaling.", founders: [ { - id: "founder-20", - name: "John Doe", - role: "SWE", + id: "founder-5", + name: "David Park", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-21", - name: "John Doe", - role: "SWE", + id: "founder-6", + name: "Emily Watson", + role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Winter 2025 Product Studio Cohort", + sectionOrder: 22, + }, + { + id: "project-10", + title: "FoodShare", + description: "Food rescue and redistribution platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "FoodShare Foundation", + companyWebsite: "https://foodshare.org", + categories: ["Sustainability", "Social Impact", "Non-profit"], + overview: "FoodShare connects food donors with organizations that need it. Our platform reduces food waste while helping feed communities in need. Restaurants, events, and grocery stores can easily donate surplus food.", + founders: [ + { + id: "founder-17", + name: "Emma Johnson", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2025 Product Studio Cohort", + sectionOrder: 21, }, { id: "project-11", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + title: "CampusGuide", + description: "Augmented reality campus navigation app", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 3"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "CampusGuide Inc.", + companyWebsite: "https://campusguide.com", + categories: ["Mobile", "AR/VR", "Consumer"], + overview: "CampusGuide uses augmented reality to help students navigate campus. Point your camera at buildings to get information, find your classes, discover resources, and explore campus with ease.", founders: [ { - id: "founder-22", - name: "John Doe", - role: "SWE", + id: "founder-18", + name: "Chris Lee", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-19", + name: "Amanda Chen", + role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, }, { - id: "project-12", - title: "Lorem Ipsum Title", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + id: "project-13", + title: "FinanceHub", + description: "Personal finance tracking for college students", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "LOREM SOME COMPANY", - companyWebsite: "https://example.com", - categories: ["Category 1", "Category 2"], - overview: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + companyName: "FinanceHub Inc.", + companyWebsite: "https://financehub.com", + categories: ["FinTech", "Consumer", "B2C"], + overview: "FinanceHub helps college students manage their finances with features tailored to student life. Track expenses, budget for tuition, and learn financial literacy in an easy-to-use app.", founders: [ { - id: "founder-23", - name: "John Doe", - role: "SWE", + id: "founder-10", + name: "Rachel Torres", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-11", + name: "James Wilson", + role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", }, + ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, + }, + { + id: "project-14", + title: "EventSpace", + description: "Campus event discovery and ticketing platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "EventSpace Inc.", + companyWebsite: "https://eventspace.com", + categories: ["Events", "Social", "B2C"], + overview: "EventSpace makes it easy to discover and attend campus events. Students can find events, buy tickets, and coordinate with friends. Event organizers get tools to manage and promote their events.", + founders: [ { - id: "founder-24", - name: "John Doe", - role: "SWE", + id: "founder-12", + name: "Kevin Patel", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, + }, + { + id: "project-15", + title: "StudyBuddy", + description: "AI-powered study partner and flashcard generator", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "StudyBuddy AI", + companyWebsite: "https://studybuddy.ai", + categories: ["EdTech", "AI/ML", "B2C"], + overview: "StudyBuddy uses AI to generate flashcards from your notes, quiz you on key concepts, and track your learning progress. It's like having a personalized tutor available 24/7.", + founders: [ + { + id: "founder-13", + name: "Nina Williams", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-14", + name: "Tom Anderson", + role: "CTO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-16", + title: "FoodShare", + description: "Food rescue and redistribution platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "FoodShare Foundation", + companyWebsite: "https://foodshare.org", + categories: ["Sustainability", "Social Impact", "Non-profit"], + overview: "FoodShare connects food donors with organizations that need it. Our platform reduces food waste while helping feed communities in need. Restaurants, events, and grocery stores can easily donate surplus food.", + founders: [ + { + id: "founder-17", + name: "Emma Johnson", + role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], + sectionType: "cohort", + sectionName: "Fall 2025 Product Studio Cohort", + sectionOrder: 21, }, ] diff --git a/types/project.ts b/types/project.ts index d20dba6..7a014e6 100644 --- a/types/project.ts +++ b/types/project.ts @@ -1,3 +1,5 @@ +export type SectionType = "funding" | "cohort" + export interface Founder { id: string name: string @@ -15,4 +17,7 @@ export interface Project { categories: string[] overview: string founders: Founder[] + sectionType: SectionType + sectionName: string + sectionOrder: number } From ca649b456dcd22b25f76c7097d349035a34f3766 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Thu, 22 Jan 2026 22:24:17 -0500 Subject: [PATCH 02/53] feat: proper filter bar ordering --- app/project-directory/page.tsx | 29 ++++++++++++++++++++++------- data/projects.ts | 10 +++++----- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/app/project-directory/page.tsx b/app/project-directory/page.tsx index 356c381..33820d5 100644 --- a/app/project-directory/page.tsx +++ b/app/project-directory/page.tsx @@ -122,15 +122,30 @@ export default function ProjectDirectoryPage() { } const getSectionNavData = () => { - const groups = new Map() + const groups = new Map() projects.forEach((p) => { - groups.set(p.sectionName, (groups.get(p.sectionName) || 0) + 1) + if (!groups.has(p.sectionName)) { + groups.set(p.sectionName, { + count: 0, + type: p.sectionType, + order: p.sectionOrder, + }) + } + groups.get(p.sectionName)!.count++ }) - return Array.from(groups.entries()).map(([name, count]) => ({ - name, - count, - type: projects.find((p) => p.sectionName === name)!.sectionType, - })) + return Array.from(groups.entries()) + .map(([name, data]) => ({ + name, + count: data.count, + type: data.type, + order: data.order, + })) + .sort((a, b) => { + if (a.type !== b.type) return a.type === "funding" ? -1 : 1 + // For funding sections, lower order = higher priority (Y-Combinator = 1 comes first) + // For cohort sections, higher order = more recent (Winter 2026 = 23 comes first) + return a.type === "funding" ? a.order - b.order : b.order - a.order + }) } return ( diff --git a/data/projects.ts b/data/projects.ts index 053954f..3058806 100644 --- a/data/projects.ts +++ b/data/projects.ts @@ -230,7 +230,7 @@ export const projects: Project[] = [ ], sectionType: "cohort", sectionName: "Winter 2025 Product Studio Cohort", - sectionOrder: 22, + sectionOrder: 21, }, { id: "project-10", @@ -305,7 +305,7 @@ export const projects: Project[] = [ ], sectionType: "cohort", sectionName: "Winter 2025 Product Studio Cohort", - sectionOrder: 22, + sectionOrder: 21, }, { id: "project-10", @@ -326,7 +326,7 @@ export const projects: Project[] = [ ], sectionType: "cohort", sectionName: "Fall 2025 Product Studio Cohort", - sectionOrder: 21, + sectionOrder: 22, }, { id: "project-11", @@ -428,7 +428,7 @@ export const projects: Project[] = [ ], sectionType: "cohort", sectionName: "Fall 2025 Product Studio Cohort", - sectionOrder: 21, + sectionOrder: 22, }, { id: "project-16", @@ -449,6 +449,6 @@ export const projects: Project[] = [ ], sectionType: "cohort", sectionName: "Fall 2025 Product Studio Cohort", - sectionOrder: 21, + sectionOrder: 22, }, ] From 6e46ccb1cdedba736f2240177bf09f6baa077203 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Thu, 22 Jan 2026 23:23:33 -0500 Subject: [PATCH 03/53] feat: more project data --- data/projects.ts | 692 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 542 insertions(+), 150 deletions(-) diff --git a/data/projects.ts b/data/projects.ts index 3058806..4d8a251 100644 --- a/data/projects.ts +++ b/data/projects.ts @@ -3,24 +3,30 @@ import type { Project } from "@/types/project" export const projects: Project[] = [ { id: "project-1", - title: "TechFlow AI", - description: "AI-powered workflow automation for enterprises", + title: "AgentMail", + description: "Email Inboxes for AI Agents", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "TechFlow Inc.", - companyWebsite: "https://techflow.ai", - categories: ["AI/ML", "Enterprise", "B2B"], - overview: "TechFlow AI helps enterprises automate complex workflows using artificial intelligence. Our platform integrates with existing tools and learns from your business processes to provide intelligent automation suggestions.", + companyName: "AgentMail", + companyWebsite: "https://agentmail.to", + categories: ["Developer Tools", "API", "Email"], + overview: "AgentMail is the email inbox API for AI agents. It gives agents their own email inboxes, like Gmail does for humans. Unlike other email APIs, AgentMail provides fully-managed, persistent inboxes that handle parsing, threading, labeling, searching, and replying so agents get the same email experience as humans.", founders: [ { id: "founder-1", - name: "Alex Chen", - role: "CEO", + name: "Haakam Aujla", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, { id: "founder-2", - name: "Sarah Kim", - role: "CTO", + name: "Michael Kim", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-3", + name: "Adi Singh", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, ], @@ -30,24 +36,24 @@ export const projects: Project[] = [ }, { id: "project-2", - title: "HealthSync", - description: "Personal health monitoring and insights platform", + title: "Embedder", + description: "Cursor for Firmware", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "HealthSync Labs", - companyWebsite: "https://healthsync.com", - categories: ["Healthcare", "Consumer", "B2C"], - overview: "HealthSync provides personalized health insights by integrating with wearable devices and medical records. Our AI-powered platform helps users understand their health trends and make informed decisions.", + companyName: "Embedder", + companyWebsite: "https://www.embedder.com", + categories: ["Artificial Intelligence", "Developer Tools", "Hardware"], + overview: "Embedder is a coding agent that can write, test, and debug firmware on both physical and simulated hardware. Supporting 20+ MCU/SoC platforms, it accelerates embedded systems development across semiconductors, automotive, medical devices, aerospace, IoT, and consumer electronics.", founders: [ { - id: "founder-3", - name: "Jordan Lee", + id: "founder-4", + name: "Ethan Gibbs", role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-4", - name: "Maria Rodriguez", - role: "CPO", + id: "founder-5", + name: "Bob Wei", + role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], @@ -57,6 +63,60 @@ export const projects: Project[] = [ }, { id: "project-3", + title: "Orange Slice", + description: "Agentic sales enrichment spreadsheet", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Orange Slice", + companyWebsite: "https://orangeslice.ai", + categories: ["B2B", "SaaS", "Sales", "Sales Enablement"], + overview: "Orange Slice is a spreadsheet where every column is TypeScript—and AI writes it for you. Describe what you want in chat ('find the CEO's LinkedIn and get their email'), and we generate the code, create the columns, and run it across thousands of rows. Our fully-typed SDK has 100+ enrichments built in: LinkedIn, contact info, company data, web scraping, AI research.", + founders: [ + { + id: "founder-6", + name: "Vihaar Nandigala", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-7", + name: "Kishan Sripada", + role: "CTO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "funding", + sectionName: "Y-Combinator", + sectionOrder: 1, + }, + { + id: "project-4", + title: "Skope", + description: "Build a more profitable law firm with AI", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Skope", + companyWebsite: "https://useskope.com", + categories: ["B2B", "Legal", "AI"], + overview: "Skope builds AI for core legal workflows. We will save your law firm time and help you make more money, with AI. Unlike general-purpose AI, Skope is purpose-built for legal work. We offer specialized features like document redlining and knowledge base search that other tools do not offer, as well as improved security for your data.", + founders: [ + { + id: "founder-8", + name: "Ben Smith", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-9", + name: "Connor Park", + role: "CTO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "funding", + sectionName: "Y-Combinator", + sectionOrder: 1, + }, + { + id: "project-5", title: "CloudScale", description: "Multi-cloud cost optimization and management", imageSrc: "/placeholder.svg?height=200&width=300", @@ -66,13 +126,13 @@ export const projects: Project[] = [ overview: "CloudScale helps companies optimize their cloud spending across multiple providers. Our platform provides real-time cost tracking, optimization recommendations, and automated resource scaling.", founders: [ { - id: "founder-5", + id: "founder-10", name: "David Park", role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-6", + id: "founder-11", name: "Emily Watson", role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", @@ -83,7 +143,7 @@ export const projects: Project[] = [ sectionOrder: 2, }, { - id: "project-4", + id: "project-6", title: "EduConnect", description: "Learning management for K-12 classrooms", imageSrc: "/placeholder.svg?height=200&width=300", @@ -93,7 +153,7 @@ export const projects: Project[] = [ overview: "EduConnect provides a comprehensive learning management system designed for K-12 classrooms. Our platform simplifies assignment distribution, grading, and parent communication.", founders: [ { - id: "founder-7", + id: "founder-12", name: "Lisa Chang", role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", @@ -104,7 +164,7 @@ export const projects: Project[] = [ sectionOrder: 2, }, { - id: "project-5", + id: "project-7", title: "GreenRoute", description: "Sustainable logistics and delivery optimization", imageSrc: "/placeholder.svg?height=200&width=300", @@ -114,13 +174,13 @@ export const projects: Project[] = [ overview: "GreenRoute helps delivery companies reduce their carbon footprint through intelligent route optimization. Our AI algorithms consider traffic, weather, and environmental factors to minimize emissions.", founders: [ { - id: "founder-8", + id: "founder-13", name: "Michael Brown", role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-9", + id: "founder-14", name: "Anna Green", role: "COO", imageSrc: "/placeholder.svg?height=150&width=150", @@ -131,7 +191,7 @@ export const projects: Project[] = [ sectionOrder: 23, }, { - id: "project-6", + id: "project-8", title: "FinanceHub", description: "Personal finance tracking for college students", imageSrc: "/placeholder.svg?height=200&width=300", @@ -141,13 +201,13 @@ export const projects: Project[] = [ overview: "FinanceHub helps college students manage their finances with features tailored to student life. Track expenses, budget for tuition, and learn financial literacy in an easy-to-use app.", founders: [ { - id: "founder-10", + id: "founder-15", name: "Rachel Torres", role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-11", + id: "founder-16", name: "James Wilson", role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", @@ -158,7 +218,7 @@ export const projects: Project[] = [ sectionOrder: 23, }, { - id: "project-7", + id: "project-9", title: "EventSpace", description: "Campus event discovery and ticketing platform", imageSrc: "/placeholder.svg?height=200&width=300", @@ -168,7 +228,7 @@ export const projects: Project[] = [ overview: "EventSpace makes it easy to discover and attend campus events. Students can find events, buy tickets, and coordinate with friends. Event organizers get tools to manage and promote their events.", founders: [ { - id: "founder-12", + id: "founder-17", name: "Kevin Patel", role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", @@ -179,7 +239,7 @@ export const projects: Project[] = [ sectionOrder: 23, }, { - id: "project-8", + id: "project-10", title: "StudyBuddy", description: "AI-powered study partner and flashcard generator", imageSrc: "/placeholder.svg?height=200&width=300", @@ -189,24 +249,24 @@ export const projects: Project[] = [ overview: "StudyBuddy uses AI to generate flashcards from your notes, quiz you on key concepts, and track your learning progress. It's like having a personalized tutor available 24/7.", founders: [ { - id: "founder-13", + id: "founder-18", name: "Nina Williams", role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-14", + id: "founder-19", name: "Tom Anderson", role: "CTO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], sectionType: "cohort", - sectionName: "Winter 2026 Product Studio Cohort", - sectionOrder: 23, + sectionName: "Fall 2025 Product Studio Cohort", + sectionOrder: 22, }, { - id: "project-9", + id: "project-11", title: "MentorMatch", description: "Platform connecting students with industry mentors", imageSrc: "/placeholder.svg?height=200&width=300", @@ -216,13 +276,13 @@ export const projects: Project[] = [ overview: "MentorMatch connects students with industry professionals for mentorship. Students can search for mentors in their field, schedule sessions, and build lasting professional relationships.", founders: [ { - id: "founder-15", + id: "founder-25", name: "Sofia Martinez", role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-16", + id: "founder-26", name: "Daniel Kim", role: "COO", imageSrc: "/placeholder.svg?height=150&width=150", @@ -233,7 +293,7 @@ export const projects: Project[] = [ sectionOrder: 21, }, { - id: "project-10", + id: "project-12", title: "GreenRoute", description: "Sustainable logistics and delivery optimization", imageSrc: "/placeholder.svg?height=200&width=300", @@ -243,36 +303,97 @@ export const projects: Project[] = [ overview: "GreenRoute helps delivery companies reduce their carbon footprint through intelligent route optimization. Our AI algorithms consider traffic, weather, and environmental factors to minimize emissions.", founders: [ { - id: "founder-8", + id: "founder-27", name: "Michael Brown", role: "CEO", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-9", + id: "founder-28", name: "Anna Green", role: "COO", imageSrc: "/placeholder.svg?height=150&width=150", }, ], sectionType: "cohort", + sectionName: "Winter 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-13", + title: "Lost and Found Plus", + description: "An easy-to-use system that tracks tools and materials that are being used in communal spaces", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Lost and Found Plus", + companyWebsite: "", + categories: ["Productivity", "B2B"], + overview: "An easy-to-use system that tracks tools and materials that are being used in communal spaces. Helps organizations manage shared resources efficiently and reduce loss of valuable items.", + founders: [ + { + id: "founder-25", + name: "Anuhea Tao", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, }, { - id: "project-11", - title: "EduConnect", - description: "Learning management for K-12 classrooms", + id: "project-14", + title: "Hair Haven", + description: "Our mission is to provide barbers with an individualized, interactive platform to help customers get the most optimal haircut.", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "EduConnect Systems", - companyWebsite: "https://educonnect.com", - categories: ["EdTech", "Education", "B2B"], - overview: "EduConnect provides a comprehensive learning management system designed for K-12 classrooms. Our platform simplifies assignment distribution, grading, and parent communication.", + companyName: "Hair Haven", + companyWebsite: "", + categories: ["B2B", "Beauty", "SaaS"], + overview: "Our mission is to provide barbers with an individualized, interactive platform to help customers get the most optimal haircut. Uses AI and personalization to recommend the perfect haircut based on face shape, hair type, and personal style.", + founders: [], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-15", + title: "Parrot", + description: "Advanced communication and collaboration platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Parrot", + companyWebsite: "", + categories: ["Communication", "B2B"], + overview: "Advanced communication and collaboration platform designed to enhance team productivity and information sharing in modern workplaces.", + founders: [], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-16", + title: "Quotr", + description: "Conversational AI platform that automates estimates and information gathering", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Quotr", + companyWebsite: "", + categories: ["AI/ML", "B2B", "Automation"], + overview: "Quotr is a conversational AI platform that automates away the manual calling for estimates, quotes, and general information from places all around you. All you do is put in your request, and we make the calls and get the information back to you.", founders: [ { - id: "founder-7", - name: "Lisa Chang", - role: "CEO", + id: "founder-26", + name: "Soham Salunke", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-27", + name: "Divya Ponda", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-28", + name: "Roshni Koduri", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, ], @@ -281,174 +402,445 @@ export const projects: Project[] = [ sectionOrder: 20, }, { - id: "project-12", - title: "CloudScale", - description: "Multi-cloud cost optimization and management", + id: "project-17", + title: "Manu.AI", + description: "Easy access to any manual you could ever need", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "CloudScale Inc.", - companyWebsite: "https://cloudscale.io", - categories: ["Cloud", "Enterprise", "B2B"], - overview: "CloudScale helps companies optimize their cloud spending across multiple providers. Our platform provides real-time cost tracking, optimization recommendations, and automated resource scaling.", + companyName: "Manu.AI", + companyWebsite: "", + categories: ["AI/ML", "Productivity", "B2B"], + overview: "Easy access to any manual you could ever need. AI-powered platform that provides instant access to user manuals, documentation, and guides for any product or device.", founders: [ { - id: "founder-5", - name: "David Park", - role: "CEO", + id: "founder-29", + name: "Leo Liu", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-6", - name: "Emily Watson", - role: "CTO", + id: "founder-30", + name: "Johnathan Mo", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, ], sectionType: "cohort", - sectionName: "Winter 2025 Product Studio Cohort", - sectionOrder: 21, + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, }, { - id: "project-10", - title: "FoodShare", - description: "Food rescue and redistribution platform", + id: "project-18", + title: "Closet Swap", + description: "Digital platform for borrowing and lending clothes affordably", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "FoodShare Foundation", - companyWebsite: "https://foodshare.org", - categories: ["Sustainability", "Social Impact", "Non-profit"], - overview: "FoodShare connects food donors with organizations that need it. Our platform reduces food waste while helping feed communities in need. Restaurants, events, and grocery stores can easily donate surplus food.", + companyName: "Closet Swap", + companyWebsite: "https://closet-swap.vercel.app/", + categories: ["Fashion", "B2C", "Sustainability"], + overview: "A digital platform for users to borrow and lend clothes affordably, quickly, and reliably. Reduces fashion waste while giving people access to a wider variety of clothing.", founders: [ { - id: "founder-17", - name: "Emma Johnson", - role: "CEO", + id: "founder-31", + name: "Meghna Reddy", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-32", + name: "Shriya Kankatala", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-33", + name: "Diya Mahaveer", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-34", + name: "Hana Ahmed", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, ], sectionType: "cohort", - sectionName: "Fall 2025 Product Studio Cohort", - sectionOrder: 22, + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, }, { - id: "project-11", - title: "CampusGuide", - description: "Augmented reality campus navigation app", + id: "project-19", + title: "DAWG", + description: "Elevating sport program performance through technology", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "CampusGuide Inc.", - companyWebsite: "https://campusguide.com", - categories: ["Mobile", "AR/VR", "Consumer"], - overview: "CampusGuide uses augmented reality to help students navigate campus. Point your camera at buildings to get information, find your classes, discover resources, and explore campus with ease.", + companyName: "DAWG", + companyWebsite: "", + categories: ["Sports", "B2B", "Analytics"], + overview: "Elevating sport program performance through technology. Data-driven platform that helps athletic programs optimize training, performance, and team management.", founders: [ { - id: "founder-18", - name: "Chris Lee", - role: "CEO", + id: "founder-35", + name: "Priya Gutta", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-20", + title: "Learn Bubble", + description: "Popping the bubble of higher education through accessible STEM literacy", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Learn Bubble", + companyWebsite: "", + categories: ["EdTech", "STEM", "B2C"], + overview: "Popping the bubble of higher education through accessible STEM literacy. Making complex STEM concepts accessible to everyone through interactive learning experiences.", + founders: [ { - id: "founder-19", - name: "Amanda Chen", - role: "CTO", + id: "founder-36", + name: "Rafee Mirza", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, ], sectionType: "cohort", - sectionName: "Winter 2026 Product Studio Cohort", - sectionOrder: 23, + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, }, { - id: "project-13", - title: "FinanceHub", - description: "Personal finance tracking for college students", + id: "project-21", + title: "MeetingBrew", + description: "A group event scheduler", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "FinanceHub Inc.", - companyWebsite: "https://financehub.com", - categories: ["FinTech", "Consumer", "B2C"], - overview: "FinanceHub helps college students manage their finances with features tailored to student life. Track expenses, budget for tuition, and learn financial literacy in an easy-to-use app.", + companyName: "MeetingBrew", + companyWebsite: "https://www.meetingbrew.com/", + categories: ["Productivity", "B2B", "Scheduling"], + overview: "A group event scheduler that makes coordinating meetings and events effortless. Uses smart algorithms to find optimal times that work for everyone.", founders: [ { - id: "founder-10", - name: "Rachel Torres", - role: "CEO", + id: "founder-37", + name: "Cooper Saye", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-11", - name: "James Wilson", - role: "CTO", + id: "founder-38", + name: "Brian Travis", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, ], sectionType: "cohort", - sectionName: "Winter 2026 Product Studio Cohort", - sectionOrder: 23, + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, }, { - id: "project-14", - title: "EventSpace", - description: "Campus event discovery and ticketing platform", + id: "project-22", + title: "CodeTrace", + description: "AI-powered semantic search for seamless onboarding and smarter codebase navigation", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "EventSpace Inc.", - companyWebsite: "https://eventspace.com", - categories: ["Events", "Social", "B2C"], - overview: "EventSpace makes it easy to discover and attend campus events. Students can find events, buy tickets, and coordinate with friends. Event organizers get tools to manage and promote their events.", + companyName: "CodeTrace", + companyWebsite: "https://www.codetrace.ai/", + categories: ["Developer Tools", "AI/ML", "B2B"], + overview: "AI-powered semantic search for seamless onboarding and smarter codebase navigation. Helps developers understand and navigate large codebases more efficiently.", founders: [ { - id: "founder-12", - name: "Kevin Patel", - role: "CEO", + id: "founder-39", + name: "David Mazur", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, ], sectionType: "cohort", - sectionName: "Winter 2026 Product Studio Cohort", - sectionOrder: 23, + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, }, { - id: "project-15", - title: "StudyBuddy", - description: "AI-powered study partner and flashcard generator", + id: "project-23", + title: "Doublet", + description: "Word transformation puzzle game", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "StudyBuddy AI", - companyWebsite: "https://studybuddy.ai", - categories: ["EdTech", "AI/ML", "B2C"], - overview: "StudyBuddy uses AI to generate flashcards from your notes, quiz you on key concepts, and track your learning progress. It's like having a personalized tutor available 24/7.", + companyName: "Doublet", + companyWebsite: "https://doublet-waitlist.vercel.app/", + categories: ["Gaming", "B2C", "Education"], + overview: "Word transformation puzzle game that challenges players to transform one word into another through a series of steps. Educational and entertaining word game.", founders: [ { - id: "founder-13", - name: "Nina Williams", - role: "CEO", + id: "founder-40", + name: "Adviti Mishra", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, { - id: "founder-14", - name: "Tom Anderson", - role: "CTO", + id: "founder-41", + name: "Isha Kalwani", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, ], sectionType: "cohort", - sectionName: "Fall 2025 Product Studio Cohort", - sectionOrder: 22, + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, }, { - id: "project-16", - title: "FoodShare", - description: "Food rescue and redistribution platform", + id: "project-24", + title: "Batch", + description: "Shared rides to DTW from $9", imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "FoodShare Foundation", - companyWebsite: "https://foodshare.org", - categories: ["Sustainability", "Social Impact", "Non-profit"], - overview: "FoodShare connects food donors with organizations that need it. Our platform reduces food waste while helping feed communities in need. Restaurants, events, and grocery stores can easily donate surplus food.", + companyName: "Batch", + companyWebsite: "https://www.batchme.app/", + categories: ["Transportation", "B2C", "Sharing Economy"], + overview: "Ubers to DTW from $9. Batch your ride with verified U-M students. Affordable shared transportation solution for university students traveling to the airport.", founders: [ { - id: "founder-17", - name: "Emma Johnson", - role: "CEO", + id: "founder-42", + name: "Sanjit Vijay", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-43", + name: "Aryan Shah", + role: "Founder", imageSrc: "/placeholder.svg?height=150&width=150", }, ], sectionType: "cohort", - sectionName: "Fall 2025 Product Studio Cohort", - sectionOrder: 22, + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-25", + title: "Find Blue", + description: "The go-to space for students at the University of Michigan to launch ideas and build ventures", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Find Blue", + companyWebsite: "https://f-bweb.vercel.app/", + categories: ["Community", "B2C", "Entrepreneurship"], + overview: "The go-to space for students at the University of Michigan to launch ideas and build ventures. Community platform for student entrepreneurs and innovators.", + founders: [ + { + id: "founder-44", + name: "Owen Lennon", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-45", + name: "Rakesh Varma Kottapalli", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-26", + title: "Parley", + description: "Gamify productivity by betting on your friend's tasks", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Parley", + companyWebsite: "", + categories: ["Productivity", "B2C", "Gamification"], + overview: "Gamify productivity by betting on your friend's tasks. Social accountability platform that turns task completion into friendly competitions and bets.", + founders: [ + { + id: "founder-46", + name: "Toan Bui", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-47", + name: "Sriram MK", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-48", + name: "Aditi Locula", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-27", + title: "Courtline", + description: "Organizing recreational sports & pickup games at UMich", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Courtline", + companyWebsite: "https://mqueue-six.vercel.app/", + categories: ["Sports", "B2C", "Community"], + overview: "Organizing recreational sports & pickup games at UMich! Platform for coordinating and joining casual sports activities and pickup games on campus.", + founders: [ + { + id: "founder-49", + name: "Amy Liu", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-50", + name: "Joshua Lee", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-28", + title: "RepConnect", + description: "App for constituents to know about local politicians", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "RepConnect", + companyWebsite: "", + categories: ["Civic Tech", "B2C", "Government"], + overview: "Ex-pollster building an app for constituents to know about local politicians 🗳️. Platform that helps citizens stay informed about their local representatives and political activities.", + founders: [ + { + id: "founder-51", + name: "Arhan Kaul", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-29", + title: "Museaic", + description: "An AI tool to automate arranging music", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Museaic", + companyWebsite: "", + categories: ["Music", "AI/ML", "B2B"], + overview: "An AI tool to automate arranging music 🎶. AI-powered platform that helps musicians and producers arrange and compose music more efficiently.", + founders: [ + { + id: "founder-52", + name: "Casey Feng", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-30", + title: "QuickSync", + description: "Helping contractors bill faster", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "QuickSync", + companyWebsite: "https://pitch.com/v/f24-demo-day-zxnxqi/50a580be-6497-435e-9e3d-4516bcde4860", + categories: ["Construction", "B2B", "Productivity"], + overview: "Helping contractors bill faster. Streamlined billing and invoicing platform designed specifically for construction contractors.", + founders: [ + { + id: "founder-53", + name: "Phoenix Sheppard", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-31", + title: "Infralens", + description: "All-In-One 3D Imaging Access for Contractors", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Infralens", + companyWebsite: "https://pitch.com/v/f24-demo-day-zxnxqi/aa63d81b-fd08-4e46-9f50-6f5d4d1cbf7c", + categories: ["Construction", "B2B", "3D Imaging"], + overview: "All-In-One 3D Imaging Access for Contractors. Platform that provides contractors with easy access to 3D imaging tools and data for construction projects.", + founders: [], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-32", + title: "Nudge", + description: "Helping you keep track of your must-visit travel spots", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Nudge", + companyWebsite: "", + categories: ["Travel", "B2C", "Productivity"], + overview: "Helping you keep track of your must-visit travel spots ✈️. Personal travel planning platform that helps users organize and remember places they want to visit.", + founders: [ + { + id: "founder-54", + name: "Riya Saha", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-55", + name: "Pari Gulati", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-56", + name: "Kritika Singh", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-33", + title: "Cyberwright", + description: "The first cybersecurity developer tool", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Cyberwright", + companyWebsite: "https://devpost.com/software/cyberwright", + categories: ["Security", "Developer Tools", "B2B"], + overview: "The first cybersecurity developer tool, allowing programmers to identify bugs and fix bugs before production. AI-powered security tool that helps developers find and fix security vulnerabilities in code.", + founders: [ + { + id: "founder-57", + name: "Jacob Chen", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-34", + title: "Ghost Kitchen Finder", + description: "Chrome extension that flags ghost kitchens on delivery apps", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Ghost Kitchen Finder", + companyWebsite: "", + categories: ["Browser Extension", "B2C", "Food"], + overview: "Chrome extension that flags ghost kitchens on delivery apps. Helps consumers identify and avoid ordering from virtual kitchens on food delivery platforms.", + founders: [], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, }, ] From 33a2cd3a71f4e4c5cbb9810356d6e954db3aacc0 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Thu, 22 Jan 2026 23:43:52 -0500 Subject: [PATCH 04/53] feat: better satured colors --- components/section-nav.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/components/section-nav.tsx b/components/section-nav.tsx index 6b4ef20..8ba92fd 100644 --- a/components/section-nav.tsx +++ b/components/section-nav.tsx @@ -59,12 +59,12 @@ export default function SectionNav({
) -} \ No newline at end of file +} From 7879fe544670d7566867fdac020132ba7e0e1fe7 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Thu, 22 Jan 2026 23:49:36 -0500 Subject: [PATCH 05/53] feat: turn filter into dropdown --- components/section-nav.tsx | 131 ++++++++++++++----------------------- 1 file changed, 48 insertions(+), 83 deletions(-) diff --git a/components/section-nav.tsx b/components/section-nav.tsx index 8ba92fd..18a00fe 100644 --- a/components/section-nav.tsx +++ b/components/section-nav.tsx @@ -1,7 +1,6 @@ "use client" -import { Button } from "@/components/ui/button" -import { Badge } from "@/components/ui/badge" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" interface SectionNavProps { sections: Array<{ @@ -22,19 +21,6 @@ export default function SectionNav({ onFilterChange, onScrollToSection, }: SectionNavProps) { - const handleSectionClick = (sectionName: string) => { - if (filterSection === sectionName) { - onFilterChange(null) - } else { - onFilterChange(sectionName) - } - } - - const handleAllClick = () => { - onFilterChange(null) - window.scrollTo({ top: 0, behavior: "smooth" }) - } - // Group sections by type const fundingSections = sections.filter(s => s.type === 'funding') const cohortSections = sections.filter(s => s.type === 'cohort') @@ -54,78 +40,57 @@ export default function SectionNav({ return sectionName } - return ( -
-
-
- + const handleValueChange = (value: string) => { + if (value === "all") { + onFilterChange(null) + window.scrollTo({ top: 0, behavior: "smooth" }) + } else { + onFilterChange(value) + } + } - {/* Funding sections */} - {fundingSections.map((section) => ( - - ))} + return ( +
+
+
+
) -} +} \ No newline at end of file From b13c77797515c64969b49af7d0219ac48eee9ca6 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Fri, 23 Jan 2026 08:25:21 -0500 Subject: [PATCH 06/53] fix: y-combinator -> y combinator --- data/projects.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/projects.ts b/data/projects.ts index 4d8a251..9940f98 100644 --- a/data/projects.ts +++ b/data/projects.ts @@ -31,7 +31,7 @@ export const projects: Project[] = [ }, ], sectionType: "funding", - sectionName: "Y-Combinator", + sectionName: "Y Combinator", sectionOrder: 1, }, { @@ -58,7 +58,7 @@ export const projects: Project[] = [ }, ], sectionType: "funding", - sectionName: "Y-Combinator", + sectionName: "Y Combinator", sectionOrder: 1, }, { @@ -85,7 +85,7 @@ export const projects: Project[] = [ }, ], sectionType: "funding", - sectionName: "Y-Combinator", + sectionName: "Y Combinator", sectionOrder: 1, }, { @@ -112,7 +112,7 @@ export const projects: Project[] = [ }, ], sectionType: "funding", - sectionName: "Y-Combinator", + sectionName: "Y Combinator", sectionOrder: 1, }, { From b95ee02383f57aa8c219d844a5a9a2beeb044087 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Fri, 23 Jan 2026 09:01:24 -0500 Subject: [PATCH 07/53] refactor: better project data --- data/projects.ts | 607 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 559 insertions(+), 48 deletions(-) diff --git a/data/projects.ts b/data/projects.ts index 9940f98..0d27ce4 100644 --- a/data/projects.ts +++ b/data/projects.ts @@ -115,54 +115,7 @@ export const projects: Project[] = [ sectionName: "Y Combinator", sectionOrder: 1, }, - { - id: "project-5", - title: "CloudScale", - description: "Multi-cloud cost optimization and management", - imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "CloudScale Inc.", - companyWebsite: "https://cloudscale.io", - categories: ["Cloud", "Enterprise", "B2B"], - overview: "CloudScale helps companies optimize their cloud spending across multiple providers. Our platform provides real-time cost tracking, optimization recommendations, and automated resource scaling.", - founders: [ - { - id: "founder-10", - name: "David Park", - role: "CEO", - imageSrc: "/placeholder.svg?height=150&width=150", - }, - { - id: "founder-11", - name: "Emily Watson", - role: "CTO", - imageSrc: "/placeholder.svg?height=150&width=150", - }, - ], - sectionType: "funding", - sectionName: "Other Funding", - sectionOrder: 2, - }, - { - id: "project-6", - title: "EduConnect", - description: "Learning management for K-12 classrooms", - imageSrc: "/placeholder.svg?height=200&width=300", - companyName: "EduConnect Systems", - companyWebsite: "https://educonnect.com", - categories: ["EdTech", "Education", "B2B"], - overview: "EduConnect provides a comprehensive learning management system designed for K-12 classrooms. Our platform simplifies assignment distribution, grading, and parent communication.", - founders: [ - { - id: "founder-12", - name: "Lisa Chang", - role: "CEO", - imageSrc: "/placeholder.svg?height=150&width=150", - }, - ], - sectionType: "funding", - sectionName: "Other Funding", - sectionOrder: 2, - }, + { id: "project-7", title: "GreenRoute", @@ -843,4 +796,562 @@ export const projects: Project[] = [ sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, }, + { + id: "project-35", + title: "Tour.video", + description: "Interactive virtual tours for apartments", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Tour.video", + companyWebsite: "https://tour.video/", + categories: ["Real Estate", "B2B", "Virtual Tours"], + overview: "Interactive, virtual tours that get your apartment more leases", + founders: [ + { + id: "founder-58", + name: "Kishan Sripada", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-36", + title: "MeetYourClass", + description: "Student social networking for universities", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "MeetYourClass", + companyWebsite: "https://meetyourclassinc.com/", + categories: ["Social", "B2B", "Education"], + overview: "MeetYourClass is a student-facing social networking system that helps university enrollment and student life departments better understand and reach their prospective student body.", + founders: [ + { + id: "founder-75", + name: "Jonah Liss", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-76", + name: "Blake Mischley", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-77", + name: "Kaleb Schmottlach", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-78", + name: "Jon Millar", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "funding", + sectionName: "Techstars", + sectionOrder: 6, + }, + { + id: "project-37", + title: "AccoAI", + description: "AI-powered admin task automation for practices", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "AccoAI", + companyWebsite: "https://www.accoai.com/", + categories: ["AI/ML", "B2B", "Productivity"], + overview: "AccoAI automates admin tasks and unifies your practice, helping save time and grow billables.", + founders: [ + { + id: "founder-59", + name: "Lance Fuchia", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-38", + title: "Hyperfan", + description: "Platform for creators to connect with fans", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Hyperfan", + companyWebsite: "https://www.hyperfan.com/", + categories: ["Social", "B2C", "Entertainment"], + overview: "Helping creators connect with their fans", + founders: [ + { + id: "founder-60", + name: "Areeb Khan", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-39", + title: "fomo", + description: "College event discovery platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "fomo", + companyWebsite: "", + categories: ["Events", "B2C", "Social"], + overview: "A platform to create, manage, and discover college events.", + founders: [], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-40", + title: "Shade", + description: "AI-powered media storage for creative teams", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Shade", + companyWebsite: "https://shade.inc/", + categories: ["AI/ML", "B2B", "Storage"], + overview: "Shade is a seed stage startup backed by General Catalyst, Contrary, SignalFire, and Bling building the future of file storage. AI-powered media storage solution that enables creative teams to instantly sync files from anywhere.", + founders: [ + { + id: "founder-61", + name: "Brandon Fan", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-62", + name: "Bright Xu", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-63", + name: "Edison Chiu", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-64", + name: "Gurish Sharma", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "funding", + sectionName: "General Catalyst", + sectionOrder: 4, + }, + { + id: "project-41", + title: "codefy.AI", + description: "Coding widget toolbox for developers", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "codefy.AI", + companyWebsite: "https://www.codefy.ai/", + categories: ["Developer Tools", "AI/ML", "Productivity"], + overview: "codefy.ai is your own toolbox of useful coding widgets to help you speed up your workflow. Write, explain, debug, compare, and translate code with 15+ helpful, customized tools.", + founders: [ + { + id: "founder-65", + name: "Cooper Saye", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-66", + name: "Brian Travis", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-42", + title: "wstwatch", + description: "Food waste tracking for dining halls", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "wstwatch", + companyWebsite: "", + categories: ["Sustainability", "B2B", "Food"], + overview: "wstwatch - CV food waste tracking for dining halls", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-43", + title: "frnds of frnds", + description: "Dating app based on mutual friends", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "frnds of frnds", + companyWebsite: "https://www.frndsoffrnds.com/", + categories: ["Social", "B2C", "Dating"], + overview: "LinkedIn meets Hinge: Frnds of Frnds is a dating app where your pool is mutual friends", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-44", + title: "Squad", + description: "Campus discovery platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Squad", + companyWebsite: "https://getsquad.app/", + categories: ["Community", "B2C", "Education"], + overview: "Squad is where campus comes together. Discover the best food, study spots, and more—all curated by students like you.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-45", + title: "Claris", + description: "Unified research workflow platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Claris", + companyWebsite: "https://claris-two.vercel.app/", + categories: ["Research", "B2B", "Productivity"], + overview: "Claris is a unified web platform that streamlines research workflows and save time across labs in any discipline.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-46", + title: "MBooking", + description: "Campus study space booking platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "MBooking", + companyWebsite: "https://mbooking.me", + categories: ["Productivity", "B2C", "Education"], + overview: "Find and book the perfect study space on campus without the hassle. Automated, simple, and built for students.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-47", + title: "Embedder.dev", + description: "AI tools for embedded systems engineers", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Embedder.dev", + companyWebsite: "embedder.dev", + categories: ["Developer Tools", "AI/ML", "Hardware"], + overview: "AI-native tools for embedded systems engineers", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-48", + title: "Dezu", + description: "AI tax accountant for high net worth clients", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Dezu", + companyWebsite: "https://dezu.ai/", + categories: ["AI/ML", "B2B", "Finance"], + overview: "Dezu is an AI tax accountan that automates the preparation of complex tax returns for high net worth clients.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-49", + title: "AgroQR", + description: "Environmental insights using QR sensors and drones", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "AgroQR", + companyWebsite: "https://www.useagroqr.com/", + categories: ["Agriculture", "B2B", "Sustainability"], + overview: "AgroQR delivers hyperlocal environmental insights using passive QR sensors and autonomous drones.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-50", + title: "Fathom", + description: "AI-powered runtime debugger", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Fathom", + companyWebsite: "", + categories: ["Developer Tools", "AI/ML", "Debugging"], + overview: "Fathom is an AI-powered debugger that seeks bugs during the runtime of programs while having access to its running context, memory, and stack trace.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-51", + title: "Form", + description: "Dance error detection using computer vision", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Form", + companyWebsite: "", + categories: ["AI/ML", "B2C", "Education"], + overview: "A computer vision model that maps data points to various body parts to dance videos and detects errors between a \"choreographer\" host video and a \"student\" client video.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-52", + title: "TagIt", + description: "Automated metadata tagging for sports photography", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "TagIt", + companyWebsite: "", + categories: ["AI/ML", "B2B", "Sports"], + overview: "TagIt automates metadata tagging in sports photography", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-53", + title: "TwinMind", + description: "AI sidekick for productivity", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "TwinMind", + companyWebsite: "https://twinmind.com/", + categories: ["AI/ML", "B2C", "Productivity"], + overview: "Your AI Sidekick Understands What You See and Hear to Boost Your Productivity", + founders: [], + sectionType: "funding", + sectionName: "Sequoia Capital", + sectionOrder: 3, + }, + { + id: "project-54", + title: "Altrix", + description: "Nursing station automation", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Altrix", + companyWebsite: "https://www.altrix.tech/", + categories: ["Healthcare", "B2B", "AI/ML"], + overview: "Your Nursing Station On Auto-Pilot. Save nurses 3 hours a day on administrative tasks.", + founders: [ + { + id: "founder-67", + name: "Hector Benitez Ventura", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "funding", + sectionName: "Techstars", + sectionOrder: 5, + }, + { + id: "project-55", + title: "RushLink", + description: "Live event experience for college campuses", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "RushLink", + companyWebsite: "https://download.therushlink.com", + categories: ["Events", "B2C", "Social"], + overview: "Bringing the Rush of Live Events to College Campuses", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-56", + title: "EarlyBird", + description: "Gen-Z marketing platform for new businesses", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "EarlyBird", + companyWebsite: "https://earlybirddrop.com", + categories: ["Marketing", "B2B", "SaaS"], + overview: "Gen-Z marketing platform for newly opened businesses", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-57", + title: "Nexxus Labs", + description: "Vehicle data network for urban mobility", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Nexxus Labs", + companyWebsite: "https://www.nexxuslabs.org/", + categories: ["Transportation", "B2B", "Data"], + overview: "Building a vehicle data network for the future of urban mobility", + founders: [ + { + id: "founder-68", + name: "Leo Gao", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-58", + title: "Oreen", + description: "Diagramming tool for ChatGPT", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Oreen", + companyWebsite: "https://oreen.dev", + categories: ["AI/ML", "B2C", "Productivity"], + overview: "The world's best diagramming tool for ChatGPT", + founders: [ + { + id: "founder-69", + name: "Benjamin Antonow", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-59", + title: "Scholarly", + description: "Context for research papers", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Scholarly", + companyWebsite: "", + categories: ["EdTech", "B2C", "Research"], + overview: "Scholarly provides fast, concise context for research papers. Scholarly reduces friction for people of any background trying to break into a field.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-60", + title: "Roots", + description: "Private social media for families", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Roots", + companyWebsite: "rootsapp.org", + categories: ["Social", "B2C", "Privacy"], + overview: "Roots is a private social media made specially for your family. In a world dominated by big tech selling your information, roots creates a privacy focused platform.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-61", + title: "Temp8", + description: "Dance choreography app", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Temp8", + companyWebsite: "https://www.youtube.com/watch?v=TLt6yX44u1Q&t=1s", + categories: ["Education", "B2C", "Dance"], + overview: "An app that simplifies dance by breaking down choreography into 8-counts", + founders: [ + { + id: "founder-70", + name: "Maya Malik", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-71", + name: "Jeffery Hao Wu", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-62", + title: "Milieu", + description: "Skincare platform with science", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Milieu", + companyWebsite: "https://milieubio.com", + categories: ["Healthcare", "B2C", "Beauty"], + overview: "Dedicated to revolutionizing skincare through cutting-edge science and personalized solutions.", + founders: [ + { + id: "founder-72", + name: "Dev Kunjadia", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-63", + title: "0xKnowledge", + description: "Knowledge base management with AI", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "0xKnowledge", + companyWebsite: "https://0xknowledge.io/", + categories: ["AI/ML", "B2B", "Knowledge Management"], + overview: "0xKnowledge brings cutting edge technology to your fingertips through a knowledge base management system (KBMS) that is automatically maintained, and integrated with powerful AI tools", + founders: [ + { + id: "founder-73", + name: "Joshua Sum", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-74", + name: "Ammar Ateya", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-64", + title: "Collage", + description: "AI academic advising platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Collage", + companyWebsite: "https://mycollage.us", + categories: ["EdTech", "AI/ML", "B2C"], + overview: "AI-powered academic advising and social scheduling platform for college students", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, ] From 5f7e3de44c9b8e9955e0990922c93966654f35aa Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Fri, 23 Jan 2026 09:01:55 -0500 Subject: [PATCH 08/53] feat: remove project badges --- components/project-section.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/components/project-section.tsx b/components/project-section.tsx index d97b7fa..37838c7 100644 --- a/components/project-section.tsx +++ b/components/project-section.tsx @@ -4,7 +4,6 @@ import { forwardRef } from "react" import React from "react" import ProjectCard from "./project-card" import type { Project } from "@/types/project" -import { Badge } from "@/components/ui/badge" interface ProjectSectionProps { name: string @@ -18,10 +17,7 @@ const ProjectSection = forwardRef
-

{name} ▪ {projects.length} Project{projects.length !== 1 ? "s" : ""}

- {/* */} - {/* {projects.length} project{projects.length !== 1 ? "s" : ""} */} - {/* */} +

{name}

From d58e6d7946e09c0b09669dd628d22ff5e226c6b9 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Wed, 28 Jan 2026 17:53:31 -0500 Subject: [PATCH 09/53] save --- .../components/FilterPanel.tsx | 143 ++ .../components/ProjectCard.tsx | 158 ++ .../components/ProjectDirectoryLayout.tsx | 176 ++ .../components/ProjectList.tsx | 53 + .../hooks/useProjectFilters.ts | 141 ++ app/project-directory/page.tsx | 199 +-- data/projects.ts | 230 +++ data/projects.ts.bak | 1587 +++++++++++++++++ types/project.ts | 15 + 9 files changed, 2531 insertions(+), 171 deletions(-) create mode 100644 app/project-directory/components/FilterPanel.tsx create mode 100644 app/project-directory/components/ProjectCard.tsx create mode 100644 app/project-directory/components/ProjectDirectoryLayout.tsx create mode 100644 app/project-directory/components/ProjectList.tsx create mode 100644 app/project-directory/hooks/useProjectFilters.ts create mode 100644 data/projects.ts.bak diff --git a/app/project-directory/components/FilterPanel.tsx b/app/project-directory/components/FilterPanel.tsx new file mode 100644 index 0000000..535692a --- /dev/null +++ b/app/project-directory/components/FilterPanel.tsx @@ -0,0 +1,143 @@ +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import { X, Search, Building, Users, Tag } from "lucide-react" + +interface FilterPanelProps { + filters: { + searchQuery: string + fundingSources: string[] + cohorts: string[] + categories: string[] + } + filterOptions: { + fundingSources: string[] + cohorts: string[] + categories: string[] + } + onSearchChange: (query: string) => void + onToggleFunding: (source: string) => void + onToggleCohort: (cohort: string) => void + onToggleCategory: (category: string) => void + onClearAll: () => void + hasActiveFilters: boolean +} + +export default function FilterPanel({ + filters, + filterOptions, + onSearchChange, + onToggleFunding, + onToggleCohort, + onToggleCategory, + onClearAll, + hasActiveFilters, +}: FilterPanelProps) { + return ( +
+ {/* Header */} +
+

Filters

+ {hasActiveFilters && ( + + )} +
+ + {/* Search */} +
+
+ + onSearchChange(e.target.value)} + className="pl-10" + /> +
+
+ + {/* Funding Sources */} +
+
+ +

Funding

+
+
+ {filterOptions.fundingSources.map((source) => ( + + ))} +
+
+ + {/* Product Studio Cohorts */} +
+
+ +

Cohorts

+
+
+ {filterOptions.cohorts.map((cohort) => { + const displayName = cohort.replace(" Product Studio Cohort", "") + return ( + + ) + })} +
+
+ + {/* Categories */} +
+
+ +

Categories

+
+
+ {filterOptions.categories.map((category) => ( + + ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/app/project-directory/components/ProjectCard.tsx b/app/project-directory/components/ProjectCard.tsx new file mode 100644 index 0000000..8afa235 --- /dev/null +++ b/app/project-directory/components/ProjectCard.tsx @@ -0,0 +1,158 @@ +import Image from "next/image" +import type { Project } from "@/types/project" + +interface ProjectCardProps { + project: Project + onClick: () => void +} + +// VC Fund badge colors +const vcFundColors = { + "Y Combinator": "bg-orange-500 text-white", + "Techstars": "bg-blue-500 text-white", + "General Catalyst": "bg-green-500 text-white", + "Sequoia Capital": "bg-red-500 text-white", +} + +// Get cohort display name (remove "Product Studio Cohort" suffix) +const getCohortDisplayName = (cohortName: string) => { + return cohortName.replace(" Product Studio Cohort", "") +} + +export default function ProjectCard({ project, onClick }: ProjectCardProps) { + const hasVCFunding = project.sectionType === "funding" + const hasCohort = project.sectionType === "cohort" + + const vcBadgeColor = vcFundColors[project.sectionName as keyof typeof vcFundColors] || "bg-gray-500 text-white" + + return ( +
+
+ {/* Company Logo/Image */} +
+ {project.companyName} +
+ + {/* Project Info */} +
+

+ {project.companyName} +

+

+ {project.title} +

+

+ {project.description} +

+ + {/* Badges */} +
+ {hasVCFunding && ( + + {project.sectionName} + + )} + {hasCohort && ( + + {getCohortDisplayName(project.sectionName)} + + )} +
+ + {/* Category Tags */} + {project.categories.length > 0 && ( +
+ {project.categories.slice(0, 3).map((category) => ( + + {category} + + ))} + {project.categories.length > 3 && ( + + +{project.categories.length - 3} + + )} +
+ )} + + {/* Investors Preview */} + {project.investors && project.investors.length > 0 && ( +
+
+ Investors: +
+ {project.investors.slice(0, 3).map((investor) => ( + investor.website ? ( + + {investor.name} + + + + + ) : ( + + {investor.name} + + ) + ))} + {project.investors.length > 3 && ( + + +{project.investors.length - 3} + + )} +
+
+
+ )} + + {/* Founders Preview */} + {project.founders.length > 0 && ( +
+
+ {project.founders.slice(0, 2).map((founder) => ( +
+ {founder.name} +
+ ))} +
+ + {project.founders.length === 1 + ? project.founders[0].name + : `${project.founders[0].name} +${project.founders.length - 1}` + } + +
+ )} +
+
+
+ ) +} \ No newline at end of file diff --git a/app/project-directory/components/ProjectDirectoryLayout.tsx b/app/project-directory/components/ProjectDirectoryLayout.tsx new file mode 100644 index 0000000..47733c2 --- /dev/null +++ b/app/project-directory/components/ProjectDirectoryLayout.tsx @@ -0,0 +1,176 @@ +import { useState } from "react" +import FilterPanel from "./FilterPanel" +import ProjectList from "./ProjectList" +import { Button } from "@/components/ui/button" +import { Filter } from "lucide-react" +import type { Project } from "@/types/project" + +interface ProjectDirectoryLayoutProps { + projects: Project[] + filters: { + searchQuery: string + fundingSources: string[] + cohorts: string[] + categories: string[] + } + filterOptions: { + fundingSources: string[] + cohorts: string[] + categories: string[] + } + onSearchChange: (query: string) => void + onToggleFunding: (source: string) => void + onToggleCohort: (cohort: string) => void + onToggleCategory: (category: string) => void + onClearAll: () => void + hasActiveFilters: boolean + onProjectClick: (project: Project) => void +} + +export default function ProjectDirectoryLayout({ + projects, + filters, + filterOptions, + onSearchChange, + onToggleFunding, + onToggleCohort, + onToggleCategory, + onClearAll, + hasActiveFilters, + onProjectClick, +}: ProjectDirectoryLayoutProps) { + const [isMobileFilterOpen, setIsMobileFilterOpen] = useState(false) + + return ( + <> + {/* Desktop Layout */} +
+ {/* Filter Panel - Left Side */} +
+
+ +
+
+ + {/* Project List - Right Side */} +
+
+

+ Projects ({projects.length}) +

+ {hasActiveFilters && ( + + )} +
+ +
+
+ + {/* Mobile/Tablet Layout */} +
+ {/* Mobile Filter Button */} +
+
+ onSearchChange(e.target.value)} + className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" + /> +
+ +
+ + {/* Project Count */} +
+

+ Projects ({projects.length}) +

+
+ + {/* Project List */} + + + {/* Mobile Filter Modal */} + {isMobileFilterOpen && ( +
+ {/* Backdrop */} +
setIsMobileFilterOpen(false)} + /> + + {/* Filter Panel Slide-up */} +
+ {/* Header */} +
+

Filters

+ +
+ + {/* Filter Content */} +
+ +
+ + {/* Footer */} +
+ +
+
+
+ )} +
+ + ) +} \ No newline at end of file diff --git a/app/project-directory/components/ProjectList.tsx b/app/project-directory/components/ProjectList.tsx new file mode 100644 index 0000000..c80e084 --- /dev/null +++ b/app/project-directory/components/ProjectList.tsx @@ -0,0 +1,53 @@ +import { useMemo } from "react" +import ProjectCard from "./ProjectCard" +import type { Project } from "@/types/project" + +interface ProjectListProps { + projects: Project[] + onProjectClick: (project: Project) => void +} + +export default function ProjectList({ projects, onProjectClick }: ProjectListProps) { + // Sort projects: funding projects first, then cohorts, maintaining existing order logic + const sortedProjects = useMemo(() => { + return [...projects].sort((a, b) => { + // If both are same type, sort by section order + if (a.sectionType === b.sectionType) { + if (a.sectionType === "funding") { + // For funding, lower order = higher priority + return a.sectionOrder - b.sectionOrder + } else { + // For cohorts, higher order = more recent + return b.sectionOrder - a.sectionOrder + } + } + // Funding projects come first + return a.sectionType === "funding" ? -1 : 1 + }) + }, [projects]) + + if (projects.length === 0) { + return ( +
+
+

No projects found

+

+ Try adjusting your filters or search terms +

+
+
+ ) + } + + return ( +
+ {sortedProjects.map((project) => ( + onProjectClick(project)} + /> + ))} +
+ ) +} \ No newline at end of file diff --git a/app/project-directory/hooks/useProjectFilters.ts b/app/project-directory/hooks/useProjectFilters.ts new file mode 100644 index 0000000..40ba806 --- /dev/null +++ b/app/project-directory/hooks/useProjectFilters.ts @@ -0,0 +1,141 @@ +import { useState, useMemo } from "react" +import { projects } from "@/data/projects" +import type { Project, SectionType } from "@/types/project" + +export interface FilterState { + searchQuery: string + fundingSources: string[] + cohorts: string[] + categories: string[] +} + +export const useProjectFilters = () => { + const [filters, setFilters] = useState({ + searchQuery: "", + fundingSources: [], + cohorts: [], + categories: [], + }) + + // Get all available filter options + const filterOptions = useMemo(() => { + const fundingSources = new Set() + const cohorts = new Set() + const categories = new Set() + + projects.forEach((project) => { + if (project.sectionType === "funding") { + fundingSources.add(project.sectionName) + } else if (project.sectionType === "cohort") { + cohorts.add(project.sectionName) + } + project.categories.forEach((category) => categories.add(category)) + }) + + return { + fundingSources: Array.from(fundingSources).sort(), + cohorts: Array.from(cohorts).sort((a, b) => { + // Sort cohorts by recency (most recent first) + const cohortOrder = { + "Winter 2026 Product Studio Cohort": 23, + "Fall 2025 Product Studio Cohort": 22, + "Spring 2025 Product Studio Cohort": 21, + "Winter 2025 Product Studio Cohort": 21, + "Fall 2024 Product Studio Cohort": 20, + } + const orderA = cohortOrder[a as keyof typeof cohortOrder] || 0 + const orderB = cohortOrder[b as keyof typeof cohortOrder] || 0 + return orderB - orderA + }), + categories: Array.from(categories).sort(), + } + }, []) + + // Filter projects based on current filter state + const filteredProjects = useMemo(() => { + return projects.filter((project) => { + // Search filter + const matchesSearch = + filters.searchQuery === "" || + project.title.toLowerCase().includes(filters.searchQuery.toLowerCase()) || + project.companyName.toLowerCase().includes(filters.searchQuery.toLowerCase()) + + // Funding source filter + const matchesFunding = + filters.fundingSources.length === 0 || + (project.sectionType === "funding" && filters.fundingSources.includes(project.sectionName)) + + // Cohort filter + const matchesCohort = + filters.cohorts.length === 0 || + (project.sectionType === "cohort" && filters.cohorts.includes(project.sectionName)) + + // Category filter + const matchesCategory = + filters.categories.length === 0 || + project.categories.some((category) => filters.categories.includes(category)) + + return matchesSearch && matchesFunding && matchesCohort && matchesCategory + }) + }, [filters]) + + // Update filter functions + const updateFilters = (newFilters: Partial) => { + setFilters((prev) => ({ ...prev, ...newFilters })) + } + + const setSearchQuery = (query: string) => { + updateFilters({ searchQuery: query }) + } + + const toggleFundingSource = (source: string) => { + const current = filters.fundingSources + const updated = current.includes(source) + ? current.filter((s) => s !== source) + : [...current, source] + updateFilters({ fundingSources: updated }) + } + + const toggleCohort = (cohort: string) => { + const current = filters.cohorts + const updated = current.includes(cohort) + ? current.filter((c) => c !== cohort) + : [...current, cohort] + updateFilters({ cohorts: updated }) + } + + const toggleCategory = (category: string) => { + const current = filters.categories + const updated = current.includes(category) + ? current.filter((c) => c !== category) + : [...current, category] + updateFilters({ categories: updated }) + } + + const clearAllFilters = () => { + setFilters({ + searchQuery: "", + fundingSources: [], + cohorts: [], + categories: [], + }) + } + + const hasActiveFilters = + filters.searchQuery !== "" || + filters.fundingSources.length > 0 || + filters.cohorts.length > 0 || + filters.categories.length > 0 + + return { + filters, + filteredProjects, + filterOptions, + setSearchQuery, + toggleFundingSource, + toggleCohort, + toggleCategory, + clearAllFilters, + hasActiveFilters, + } +} \ No newline at end of file diff --git a/app/project-directory/page.tsx b/app/project-directory/page.tsx index 33820d5..cdee3a0 100644 --- a/app/project-directory/page.tsx +++ b/app/project-directory/page.tsx @@ -1,101 +1,28 @@ "use client" -import { useState, useMemo, useEffect, useRef } from "react" +import { useState } from "react" import Header from "@/components/header" import Footer from "@/components/footer" import ProjectModal from "@/components/project-modal" -import SectionNav from "@/components/section-nav" -import ProjectSection from "@/components/project-section" -import { projects } from "@/data/projects" -import { Input } from "@/components/ui/input" +import ProjectDirectoryLayout from "./components/ProjectDirectoryLayout" +import { useProjectFilters } from "./hooks/useProjectFilters" import type { Project } from "@/types/project" export default function ProjectDirectoryPage() { const [selectedProject, setSelectedProject] = useState(null) const [isModalOpen, setIsModalOpen] = useState(false) - const [searchQuery, setSearchQuery] = useState("") - const [activeSection, setActiveSection] = useState(null) - const [filterSection, setFilterSection] = useState(null) - const observerRef = useRef(null) - const sectionRefs = useRef>(new Map()) - const filteredProjects = useMemo(() => { - return projects.filter((project) => { - const matchesSearch = searchQuery === "" || project.title.toLowerCase().includes(searchQuery.toLowerCase()) - const matchesSection = filterSection === null || project.sectionName === filterSection - return matchesSearch && matchesSection - }) - }, [searchQuery, filterSection]) - - const sections = useMemo(() => { - const groups = new Map() - filteredProjects.forEach((project) => { - if (!groups.has(project.sectionName)) { - groups.set(project.sectionName, { - projects: [], - type: project.sectionType, - order: project.sectionOrder, - }) - } - groups.get(project.sectionName)!.projects.push(project) - }) - - return Array.from(groups.entries()) - .map(([name, data]) => ({ - name, - count: data.projects.length, - type: data.type, - order: data.order, - projects: data.projects, - })) - .sort((a, b) => { - if (a.type !== b.type) return a.type === "funding" ? -1 : 1 - // For funding sections, lower order = higher priority (Y-Combinator = 1 comes first) - // For cohort sections, higher order = more recent (Winter 2026 = 23 comes first) - return a.type === "funding" ? a.order - b.order : b.order - a.order - }) - }, [filteredProjects]) - - useEffect(() => { - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - const sectionName = entry.target.getAttribute("data-section-name") - if (sectionName) { - setActiveSection(sectionName) - } - } - }) - }, - { - rootMargin: "-20% 0px -60% 0px", - threshold: 0, - } - ) - - observerRef.current = observer - - return () => { - observer.disconnect() - } - }, []) - - useEffect(() => { - sectionRefs.current.forEach((ref) => { - if (observerRef.current) { - observerRef.current.observe(ref) - } - }) - - return () => { - sectionRefs.current.forEach((ref) => { - if (observerRef.current) { - observerRef.current.unobserve(ref) - } - }) - } - }, [sections]) + const { + filters, + filteredProjects, + filterOptions, + setSearchQuery, + toggleFundingSource, + toggleCohort, + toggleCategory, + clearAllFilters, + hasActiveFilters, + } = useProjectFilters() const openProjectModal = (project: Project) => { setSelectedProject(project) @@ -106,53 +33,12 @@ export default function ProjectDirectoryPage() { setIsModalOpen(false) } - const scrollToSection = (sectionName: string) => { - const element = sectionRefs.current.get(sectionName) - if (element) { - element.scrollIntoView({ behavior: "smooth", block: "start" }) - } - } - - const handleFilterChange = (section: string | null) => { - setFilterSection(section) - } - - const isSectionVisible = (sectionName: string) => { - return filterSection === null || filterSection === sectionName - } - - const getSectionNavData = () => { - const groups = new Map() - projects.forEach((p) => { - if (!groups.has(p.sectionName)) { - groups.set(p.sectionName, { - count: 0, - type: p.sectionType, - order: p.sectionOrder, - }) - } - groups.get(p.sectionName)!.count++ - }) - return Array.from(groups.entries()) - .map(([name, data]) => ({ - name, - count: data.count, - type: data.type, - order: data.order, - })) - .sort((a, b) => { - if (a.type !== b.type) return a.type === "funding" ? -1 : 1 - // For funding sections, lower order = higher priority (Y-Combinator = 1 comes first) - // For cohort sections, higher order = more recent (Winter 2026 = 23 comes first) - return a.type === "funding" ? a.order - b.order : b.order - a.order - }) - } - return (
-
+
+ {/* Header */}

Project Directory

@@ -160,48 +46,19 @@ export default function ProjectDirectoryPage() {

-
- setSearchQuery(e.target.value)} - className="max-w-md" - /> -
- - - - {sections.length > 0 ? ( - sections.map((section) => { - if (!isSectionVisible(section.name)) return null - return ( - { - if (el) { - sectionRefs.current.set(section.name, el) - } - }} - data-section-name={section.name} - name={section.name} - projects={section.projects} - onProjectClick={openProjectModal} - /> - ) - }) - ) : ( -
-

No projects found matching your criteria.

-

Try adjusting your search or filters.

-
- )}
diff --git a/data/projects.ts b/data/projects.ts index 0d27ce4..4ef9452 100644 --- a/data/projects.ts +++ b/data/projects.ts @@ -30,6 +30,48 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + investors: [ + { + id: "investor-1", + name: "Y Combinator", + type: "accelerator", + website: "https://www.ycombinator.com" + }, + { + id: "investor-2", + name: "General Catalyst", + type: "vc", + website: "https://www.generalcatalyst.com" + }, + { + id: "investor-3", + name: "Agent Fund", + type: "vc" + }, + { + id: "investor-4", + name: "Pioneer Fund", + type: "vc", + website: "https://www.pioneer.vc" + }, + { + id: "investor-5", + name: "Paul Graham", + type: "angel" + }, + { + id: "investor-6", + name: "Paul Copplestone", + type: "angel" + }, + { + id: "investor-7", + name: "Karim Atiyeh", + type: "angel" + } + ], + fundingAmount: 500000, + fundingStage: "pre-seed", sectionType: "funding", sectionName: "Y Combinator", sectionOrder: 1, @@ -57,6 +99,21 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + investors: [ + { + id: "investor-8", + name: "Y Combinator", + type: "accelerator", + website: "https://www.ycombinator.com" + }, + { + id: "investor-9", + name: "Z Fellows", + type: "accelerator" + } + ], + fundingAmount: 50000, + fundingStage: "seed", sectionType: "funding", sectionName: "Y Combinator", sectionOrder: 1, @@ -84,6 +141,35 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + investors: [ + { + id: "investor-10", + name: "Y Combinator", + type: "accelerator", + website: "https://www.ycombinator.com" + }, + { + id: "investor-11", + name: "Lobster Capital", + type: "vc" + }, + { + id: "investor-12", + name: "Moxxie Ventures", + type: "vc" + }, + { + id: "investor-13", + name: "Transpose Platform Management", + type: "vc" + }, + { + id: "investor-14", + name: "Paul Graham", + type: "angel" + } + ], + fundingStage: "seed", sectionType: "funding", sectionName: "Y Combinator", sectionOrder: 1, @@ -111,6 +197,15 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + investors: [ + { + id: "investor-15", + name: "Y Combinator", + type: "accelerator", + website: "https://www.ycombinator.com" + } + ], + fundingStage: "seed", sectionType: "funding", sectionName: "Y Combinator", sectionOrder: 1, @@ -289,6 +384,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -303,6 +399,7 @@ export const projects: Project[] = [ categories: ["B2B", "Beauty", "SaaS"], overview: "Our mission is to provide barbers with an individualized, interactive platform to help customers get the most optimal haircut. Uses AI and personalization to recommend the perfect haircut based on face shape, hair type, and personal style.", founders: [], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -317,6 +414,7 @@ export const projects: Project[] = [ categories: ["Communication", "B2B"], overview: "Advanced communication and collaboration platform designed to enhance team productivity and information sharing in modern workplaces.", founders: [], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -350,6 +448,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -377,6 +476,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -416,6 +516,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -437,6 +538,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -458,6 +560,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -485,6 +588,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -506,6 +610,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -533,6 +638,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -560,6 +666,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -587,6 +694,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -620,6 +728,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -647,6 +756,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -668,6 +778,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -689,6 +800,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -710,6 +822,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -724,6 +837,7 @@ export const projects: Project[] = [ categories: ["Construction", "B2B", "3D Imaging"], overview: "All-In-One 3D Imaging Access for Contractors. Platform that provides contractors with easy access to 3D imaging tools and data for construction projects.", founders: [], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -757,6 +871,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -778,6 +893,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -792,6 +908,7 @@ export const projects: Project[] = [ categories: ["Browser Extension", "B2C", "Food"], overview: "Chrome extension that flags ghost kitchens on delivery apps. Helps consumers identify and avoid ordering from virtual kitchens on food delivery platforms.", founders: [], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -813,6 +930,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -852,6 +970,22 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + investors: [ + { + id: "investor-26", + name: "Techstars Detroit Powered by J.P. Morgan", + type: "accelerator", + website: "https://www.techstars.com" + }, + { + id: "investor-27", + name: "Zell Lurie Institute", + type: "university-fund", + website: "https://zli.umich.edu" + } + ], + fundingAmount: 120000, + fundingStage: "seed", sectionType: "funding", sectionName: "Techstars", sectionOrder: 6, @@ -873,6 +1007,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -894,6 +1029,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -908,6 +1044,7 @@ export const projects: Project[] = [ categories: ["Events", "B2C", "Social"], overview: "A platform to create, manage, and discover college events.", founders: [], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -947,6 +1084,47 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + investors: [ + { + id: "investor-16", + name: "General Catalyst", + type: "vc", + website: "https://www.generalcatalyst.com" + }, + { + id: "investor-17", + name: "Contrary Capital", + type: "vc", + website: "https://www.contrary.com" + }, + { + id: "investor-18", + name: "SignalFire", + type: "vc", + website: "https://www.signalfire.com" + }, + { + id: "investor-19", + name: "Bling", + type: "vc" + }, + { + id: "investor-20", + name: "GC Venture Fellows", + type: "vc" + }, + { + id: "investor-21", + name: "Gold House Ventures", + type: "vc" + }, + { + id: "investor-22", + name: "Z Fellows", + type: "accelerator" + } + ], + fundingStage: "seed", sectionType: "funding", sectionName: "General Catalyst", sectionOrder: 4, @@ -974,6 +1152,7 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + fundingStage: "university", sectionType: "cohort", sectionName: "Fall 2024 Product Studio Cohort", sectionOrder: 20, @@ -1086,6 +1265,16 @@ export const projects: Project[] = [ categories: ["Agriculture", "B2B", "Sustainability"], overview: "AgroQR delivers hyperlocal environmental insights using passive QR sensors and autonomous drones.", founders: [], + investors: [ + { + id: "investor-31", + name: "UMich Center for Entrepreneurship", + type: "university-fund", + website: "https://cfe.umich.edu" + } + ], + fundingAmount: 5000, + fundingStage: "university", sectionType: "cohort", sectionName: "Spring 2025 Product Studio Cohort", sectionOrder: 21, @@ -1142,6 +1331,27 @@ export const projects: Project[] = [ categories: ["AI/ML", "B2C", "Productivity"], overview: "Your AI Sidekick Understands What You See and Hear to Boost Your Productivity", founders: [], + investors: [ + { + id: "investor-23", + name: "Streamlined Ventures", + type: "vc" + }, + { + id: "investor-24", + name: "Sequoia Capital", + type: "vc", + website: "https://www.sequoiacap.com" + }, + { + id: "investor-25", + name: "Stephen Wolfram", + type: "angel" + } + ], + fundingAmount: 5700000, + fundingStage: "seed", + valuation: 60000000, sectionType: "funding", sectionName: "Sequoia Capital", sectionOrder: 3, @@ -1163,6 +1373,26 @@ export const projects: Project[] = [ imageSrc: "/placeholder.svg?height=150&width=150", }, ], + investors: [ + { + id: "investor-28", + name: "Techstars Health AI Baltimore", + type: "accelerator", + website: "https://www.techstars.com" + }, + { + id: "investor-29", + name: "Armajaro Holdings", + type: "vc" + }, + { + id: "investor-30", + name: "Heliconia Capital", + type: "vc" + } + ], + fundingAmount: 300000, + fundingStage: "seed", sectionType: "funding", sectionName: "Techstars", sectionOrder: 5, diff --git a/data/projects.ts.bak b/data/projects.ts.bak new file mode 100644 index 0000000..4ef9452 --- /dev/null +++ b/data/projects.ts.bak @@ -0,0 +1,1587 @@ +import type { Project } from "@/types/project" + +export const projects: Project[] = [ + { + id: "project-1", + title: "AgentMail", + description: "Email Inboxes for AI Agents", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "AgentMail", + companyWebsite: "https://agentmail.to", + categories: ["Developer Tools", "API", "Email"], + overview: "AgentMail is the email inbox API for AI agents. It gives agents their own email inboxes, like Gmail does for humans. Unlike other email APIs, AgentMail provides fully-managed, persistent inboxes that handle parsing, threading, labeling, searching, and replying so agents get the same email experience as humans.", + founders: [ + { + id: "founder-1", + name: "Haakam Aujla", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-2", + name: "Michael Kim", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-3", + name: "Adi Singh", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + investors: [ + { + id: "investor-1", + name: "Y Combinator", + type: "accelerator", + website: "https://www.ycombinator.com" + }, + { + id: "investor-2", + name: "General Catalyst", + type: "vc", + website: "https://www.generalcatalyst.com" + }, + { + id: "investor-3", + name: "Agent Fund", + type: "vc" + }, + { + id: "investor-4", + name: "Pioneer Fund", + type: "vc", + website: "https://www.pioneer.vc" + }, + { + id: "investor-5", + name: "Paul Graham", + type: "angel" + }, + { + id: "investor-6", + name: "Paul Copplestone", + type: "angel" + }, + { + id: "investor-7", + name: "Karim Atiyeh", + type: "angel" + } + ], + fundingAmount: 500000, + fundingStage: "pre-seed", + sectionType: "funding", + sectionName: "Y Combinator", + sectionOrder: 1, + }, + { + id: "project-2", + title: "Embedder", + description: "Cursor for Firmware", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Embedder", + companyWebsite: "https://www.embedder.com", + categories: ["Artificial Intelligence", "Developer Tools", "Hardware"], + overview: "Embedder is a coding agent that can write, test, and debug firmware on both physical and simulated hardware. Supporting 20+ MCU/SoC platforms, it accelerates embedded systems development across semiconductors, automotive, medical devices, aerospace, IoT, and consumer electronics.", + founders: [ + { + id: "founder-4", + name: "Ethan Gibbs", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-5", + name: "Bob Wei", + role: "CTO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + investors: [ + { + id: "investor-8", + name: "Y Combinator", + type: "accelerator", + website: "https://www.ycombinator.com" + }, + { + id: "investor-9", + name: "Z Fellows", + type: "accelerator" + } + ], + fundingAmount: 50000, + fundingStage: "seed", + sectionType: "funding", + sectionName: "Y Combinator", + sectionOrder: 1, + }, + { + id: "project-3", + title: "Orange Slice", + description: "Agentic sales enrichment spreadsheet", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Orange Slice", + companyWebsite: "https://orangeslice.ai", + categories: ["B2B", "SaaS", "Sales", "Sales Enablement"], + overview: "Orange Slice is a spreadsheet where every column is TypeScript—and AI writes it for you. Describe what you want in chat ('find the CEO's LinkedIn and get their email'), and we generate the code, create the columns, and run it across thousands of rows. Our fully-typed SDK has 100+ enrichments built in: LinkedIn, contact info, company data, web scraping, AI research.", + founders: [ + { + id: "founder-6", + name: "Vihaar Nandigala", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-7", + name: "Kishan Sripada", + role: "CTO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + investors: [ + { + id: "investor-10", + name: "Y Combinator", + type: "accelerator", + website: "https://www.ycombinator.com" + }, + { + id: "investor-11", + name: "Lobster Capital", + type: "vc" + }, + { + id: "investor-12", + name: "Moxxie Ventures", + type: "vc" + }, + { + id: "investor-13", + name: "Transpose Platform Management", + type: "vc" + }, + { + id: "investor-14", + name: "Paul Graham", + type: "angel" + } + ], + fundingStage: "seed", + sectionType: "funding", + sectionName: "Y Combinator", + sectionOrder: 1, + }, + { + id: "project-4", + title: "Skope", + description: "Build a more profitable law firm with AI", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Skope", + companyWebsite: "https://useskope.com", + categories: ["B2B", "Legal", "AI"], + overview: "Skope builds AI for core legal workflows. We will save your law firm time and help you make more money, with AI. Unlike general-purpose AI, Skope is purpose-built for legal work. We offer specialized features like document redlining and knowledge base search that other tools do not offer, as well as improved security for your data.", + founders: [ + { + id: "founder-8", + name: "Ben Smith", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-9", + name: "Connor Park", + role: "CTO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + investors: [ + { + id: "investor-15", + name: "Y Combinator", + type: "accelerator", + website: "https://www.ycombinator.com" + } + ], + fundingStage: "seed", + sectionType: "funding", + sectionName: "Y Combinator", + sectionOrder: 1, + }, + + { + id: "project-7", + title: "GreenRoute", + description: "Sustainable logistics and delivery optimization", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "GreenRoute Logistics", + companyWebsite: "https://greenroute.io", + categories: ["Logistics", "Sustainability", "B2B"], + overview: "GreenRoute helps delivery companies reduce their carbon footprint through intelligent route optimization. Our AI algorithms consider traffic, weather, and environmental factors to minimize emissions.", + founders: [ + { + id: "founder-13", + name: "Michael Brown", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-14", + name: "Anna Green", + role: "COO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, + }, + { + id: "project-8", + title: "FinanceHub", + description: "Personal finance tracking for college students", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "FinanceHub Inc.", + companyWebsite: "https://financehub.com", + categories: ["FinTech", "Consumer", "B2C"], + overview: "FinanceHub helps college students manage their finances with features tailored to student life. Track expenses, budget for tuition, and learn financial literacy in an easy-to-use app.", + founders: [ + { + id: "founder-15", + name: "Rachel Torres", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-16", + name: "James Wilson", + role: "CTO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, + }, + { + id: "project-9", + title: "EventSpace", + description: "Campus event discovery and ticketing platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "EventSpace Inc.", + companyWebsite: "https://eventspace.com", + categories: ["Events", "Social", "B2C"], + overview: "EventSpace makes it easy to discover and attend campus events. Students can find events, buy tickets, and coordinate with friends. Event organizers get tools to manage and promote their events.", + founders: [ + { + id: "founder-17", + name: "Kevin Patel", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Winter 2026 Product Studio Cohort", + sectionOrder: 23, + }, + { + id: "project-10", + title: "StudyBuddy", + description: "AI-powered study partner and flashcard generator", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "StudyBuddy AI", + companyWebsite: "https://studybuddy.ai", + categories: ["EdTech", "AI/ML", "B2C"], + overview: "StudyBuddy uses AI to generate flashcards from your notes, quiz you on key concepts, and track your learning progress. It's like having a personalized tutor available 24/7.", + founders: [ + { + id: "founder-18", + name: "Nina Williams", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-19", + name: "Tom Anderson", + role: "CTO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Fall 2025 Product Studio Cohort", + sectionOrder: 22, + }, + { + id: "project-11", + title: "MentorMatch", + description: "Platform connecting students with industry mentors", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "MentorMatch Inc.", + companyWebsite: "https://mentormatch.com", + categories: ["Career", "Networking", "B2B"], + overview: "MentorMatch connects students with industry professionals for mentorship. Students can search for mentors in their field, schedule sessions, and build lasting professional relationships.", + founders: [ + { + id: "founder-25", + name: "Sofia Martinez", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-26", + name: "Daniel Kim", + role: "COO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Winter 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-12", + title: "GreenRoute", + description: "Sustainable logistics and delivery optimization", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "GreenRoute Logistics", + companyWebsite: "https://greenroute.io", + categories: ["Logistics", "Sustainability", "B2B"], + overview: "GreenRoute helps delivery companies reduce their carbon footprint through intelligent route optimization. Our AI algorithms consider traffic, weather, and environmental factors to minimize emissions.", + founders: [ + { + id: "founder-27", + name: "Michael Brown", + role: "CEO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-28", + name: "Anna Green", + role: "COO", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Winter 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-13", + title: "Lost and Found Plus", + description: "An easy-to-use system that tracks tools and materials that are being used in communal spaces", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Lost and Found Plus", + companyWebsite: "", + categories: ["Productivity", "B2B"], + overview: "An easy-to-use system that tracks tools and materials that are being used in communal spaces. Helps organizations manage shared resources efficiently and reduce loss of valuable items.", + founders: [ + { + id: "founder-25", + name: "Anuhea Tao", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-14", + title: "Hair Haven", + description: "Our mission is to provide barbers with an individualized, interactive platform to help customers get the most optimal haircut.", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Hair Haven", + companyWebsite: "", + categories: ["B2B", "Beauty", "SaaS"], + overview: "Our mission is to provide barbers with an individualized, interactive platform to help customers get the most optimal haircut. Uses AI and personalization to recommend the perfect haircut based on face shape, hair type, and personal style.", + founders: [], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-15", + title: "Parrot", + description: "Advanced communication and collaboration platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Parrot", + companyWebsite: "", + categories: ["Communication", "B2B"], + overview: "Advanced communication and collaboration platform designed to enhance team productivity and information sharing in modern workplaces.", + founders: [], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-16", + title: "Quotr", + description: "Conversational AI platform that automates estimates and information gathering", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Quotr", + companyWebsite: "", + categories: ["AI/ML", "B2B", "Automation"], + overview: "Quotr is a conversational AI platform that automates away the manual calling for estimates, quotes, and general information from places all around you. All you do is put in your request, and we make the calls and get the information back to you.", + founders: [ + { + id: "founder-26", + name: "Soham Salunke", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-27", + name: "Divya Ponda", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-28", + name: "Roshni Koduri", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-17", + title: "Manu.AI", + description: "Easy access to any manual you could ever need", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Manu.AI", + companyWebsite: "", + categories: ["AI/ML", "Productivity", "B2B"], + overview: "Easy access to any manual you could ever need. AI-powered platform that provides instant access to user manuals, documentation, and guides for any product or device.", + founders: [ + { + id: "founder-29", + name: "Leo Liu", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-30", + name: "Johnathan Mo", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-18", + title: "Closet Swap", + description: "Digital platform for borrowing and lending clothes affordably", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Closet Swap", + companyWebsite: "https://closet-swap.vercel.app/", + categories: ["Fashion", "B2C", "Sustainability"], + overview: "A digital platform for users to borrow and lend clothes affordably, quickly, and reliably. Reduces fashion waste while giving people access to a wider variety of clothing.", + founders: [ + { + id: "founder-31", + name: "Meghna Reddy", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-32", + name: "Shriya Kankatala", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-33", + name: "Diya Mahaveer", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-34", + name: "Hana Ahmed", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-19", + title: "DAWG", + description: "Elevating sport program performance through technology", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "DAWG", + companyWebsite: "", + categories: ["Sports", "B2B", "Analytics"], + overview: "Elevating sport program performance through technology. Data-driven platform that helps athletic programs optimize training, performance, and team management.", + founders: [ + { + id: "founder-35", + name: "Priya Gutta", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-20", + title: "Learn Bubble", + description: "Popping the bubble of higher education through accessible STEM literacy", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Learn Bubble", + companyWebsite: "", + categories: ["EdTech", "STEM", "B2C"], + overview: "Popping the bubble of higher education through accessible STEM literacy. Making complex STEM concepts accessible to everyone through interactive learning experiences.", + founders: [ + { + id: "founder-36", + name: "Rafee Mirza", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-21", + title: "MeetingBrew", + description: "A group event scheduler", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "MeetingBrew", + companyWebsite: "https://www.meetingbrew.com/", + categories: ["Productivity", "B2B", "Scheduling"], + overview: "A group event scheduler that makes coordinating meetings and events effortless. Uses smart algorithms to find optimal times that work for everyone.", + founders: [ + { + id: "founder-37", + name: "Cooper Saye", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-38", + name: "Brian Travis", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-22", + title: "CodeTrace", + description: "AI-powered semantic search for seamless onboarding and smarter codebase navigation", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "CodeTrace", + companyWebsite: "https://www.codetrace.ai/", + categories: ["Developer Tools", "AI/ML", "B2B"], + overview: "AI-powered semantic search for seamless onboarding and smarter codebase navigation. Helps developers understand and navigate large codebases more efficiently.", + founders: [ + { + id: "founder-39", + name: "David Mazur", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-23", + title: "Doublet", + description: "Word transformation puzzle game", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Doublet", + companyWebsite: "https://doublet-waitlist.vercel.app/", + categories: ["Gaming", "B2C", "Education"], + overview: "Word transformation puzzle game that challenges players to transform one word into another through a series of steps. Educational and entertaining word game.", + founders: [ + { + id: "founder-40", + name: "Adviti Mishra", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-41", + name: "Isha Kalwani", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-24", + title: "Batch", + description: "Shared rides to DTW from $9", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Batch", + companyWebsite: "https://www.batchme.app/", + categories: ["Transportation", "B2C", "Sharing Economy"], + overview: "Ubers to DTW from $9. Batch your ride with verified U-M students. Affordable shared transportation solution for university students traveling to the airport.", + founders: [ + { + id: "founder-42", + name: "Sanjit Vijay", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-43", + name: "Aryan Shah", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-25", + title: "Find Blue", + description: "The go-to space for students at the University of Michigan to launch ideas and build ventures", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Find Blue", + companyWebsite: "https://f-bweb.vercel.app/", + categories: ["Community", "B2C", "Entrepreneurship"], + overview: "The go-to space for students at the University of Michigan to launch ideas and build ventures. Community platform for student entrepreneurs and innovators.", + founders: [ + { + id: "founder-44", + name: "Owen Lennon", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-45", + name: "Rakesh Varma Kottapalli", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-26", + title: "Parley", + description: "Gamify productivity by betting on your friend's tasks", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Parley", + companyWebsite: "", + categories: ["Productivity", "B2C", "Gamification"], + overview: "Gamify productivity by betting on your friend's tasks. Social accountability platform that turns task completion into friendly competitions and bets.", + founders: [ + { + id: "founder-46", + name: "Toan Bui", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-47", + name: "Sriram MK", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-48", + name: "Aditi Locula", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-27", + title: "Courtline", + description: "Organizing recreational sports & pickup games at UMich", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Courtline", + companyWebsite: "https://mqueue-six.vercel.app/", + categories: ["Sports", "B2C", "Community"], + overview: "Organizing recreational sports & pickup games at UMich! Platform for coordinating and joining casual sports activities and pickup games on campus.", + founders: [ + { + id: "founder-49", + name: "Amy Liu", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-50", + name: "Joshua Lee", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-28", + title: "RepConnect", + description: "App for constituents to know about local politicians", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "RepConnect", + companyWebsite: "", + categories: ["Civic Tech", "B2C", "Government"], + overview: "Ex-pollster building an app for constituents to know about local politicians 🗳️. Platform that helps citizens stay informed about their local representatives and political activities.", + founders: [ + { + id: "founder-51", + name: "Arhan Kaul", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-29", + title: "Museaic", + description: "An AI tool to automate arranging music", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Museaic", + companyWebsite: "", + categories: ["Music", "AI/ML", "B2B"], + overview: "An AI tool to automate arranging music 🎶. AI-powered platform that helps musicians and producers arrange and compose music more efficiently.", + founders: [ + { + id: "founder-52", + name: "Casey Feng", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-30", + title: "QuickSync", + description: "Helping contractors bill faster", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "QuickSync", + companyWebsite: "https://pitch.com/v/f24-demo-day-zxnxqi/50a580be-6497-435e-9e3d-4516bcde4860", + categories: ["Construction", "B2B", "Productivity"], + overview: "Helping contractors bill faster. Streamlined billing and invoicing platform designed specifically for construction contractors.", + founders: [ + { + id: "founder-53", + name: "Phoenix Sheppard", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-31", + title: "Infralens", + description: "All-In-One 3D Imaging Access for Contractors", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Infralens", + companyWebsite: "https://pitch.com/v/f24-demo-day-zxnxqi/aa63d81b-fd08-4e46-9f50-6f5d4d1cbf7c", + categories: ["Construction", "B2B", "3D Imaging"], + overview: "All-In-One 3D Imaging Access for Contractors. Platform that provides contractors with easy access to 3D imaging tools and data for construction projects.", + founders: [], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-32", + title: "Nudge", + description: "Helping you keep track of your must-visit travel spots", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Nudge", + companyWebsite: "", + categories: ["Travel", "B2C", "Productivity"], + overview: "Helping you keep track of your must-visit travel spots ✈️. Personal travel planning platform that helps users organize and remember places they want to visit.", + founders: [ + { + id: "founder-54", + name: "Riya Saha", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-55", + name: "Pari Gulati", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-56", + name: "Kritika Singh", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-33", + title: "Cyberwright", + description: "The first cybersecurity developer tool", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Cyberwright", + companyWebsite: "https://devpost.com/software/cyberwright", + categories: ["Security", "Developer Tools", "B2B"], + overview: "The first cybersecurity developer tool, allowing programmers to identify bugs and fix bugs before production. AI-powered security tool that helps developers find and fix security vulnerabilities in code.", + founders: [ + { + id: "founder-57", + name: "Jacob Chen", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-34", + title: "Ghost Kitchen Finder", + description: "Chrome extension that flags ghost kitchens on delivery apps", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Ghost Kitchen Finder", + companyWebsite: "", + categories: ["Browser Extension", "B2C", "Food"], + overview: "Chrome extension that flags ghost kitchens on delivery apps. Helps consumers identify and avoid ordering from virtual kitchens on food delivery platforms.", + founders: [], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-35", + title: "Tour.video", + description: "Interactive virtual tours for apartments", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Tour.video", + companyWebsite: "https://tour.video/", + categories: ["Real Estate", "B2B", "Virtual Tours"], + overview: "Interactive, virtual tours that get your apartment more leases", + founders: [ + { + id: "founder-58", + name: "Kishan Sripada", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-36", + title: "MeetYourClass", + description: "Student social networking for universities", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "MeetYourClass", + companyWebsite: "https://meetyourclassinc.com/", + categories: ["Social", "B2B", "Education"], + overview: "MeetYourClass is a student-facing social networking system that helps university enrollment and student life departments better understand and reach their prospective student body.", + founders: [ + { + id: "founder-75", + name: "Jonah Liss", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-76", + name: "Blake Mischley", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-77", + name: "Kaleb Schmottlach", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-78", + name: "Jon Millar", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + investors: [ + { + id: "investor-26", + name: "Techstars Detroit Powered by J.P. Morgan", + type: "accelerator", + website: "https://www.techstars.com" + }, + { + id: "investor-27", + name: "Zell Lurie Institute", + type: "university-fund", + website: "https://zli.umich.edu" + } + ], + fundingAmount: 120000, + fundingStage: "seed", + sectionType: "funding", + sectionName: "Techstars", + sectionOrder: 6, + }, + { + id: "project-37", + title: "AccoAI", + description: "AI-powered admin task automation for practices", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "AccoAI", + companyWebsite: "https://www.accoai.com/", + categories: ["AI/ML", "B2B", "Productivity"], + overview: "AccoAI automates admin tasks and unifies your practice, helping save time and grow billables.", + founders: [ + { + id: "founder-59", + name: "Lance Fuchia", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-38", + title: "Hyperfan", + description: "Platform for creators to connect with fans", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Hyperfan", + companyWebsite: "https://www.hyperfan.com/", + categories: ["Social", "B2C", "Entertainment"], + overview: "Helping creators connect with their fans", + founders: [ + { + id: "founder-60", + name: "Areeb Khan", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-39", + title: "fomo", + description: "College event discovery platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "fomo", + companyWebsite: "", + categories: ["Events", "B2C", "Social"], + overview: "A platform to create, manage, and discover college events.", + founders: [], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-40", + title: "Shade", + description: "AI-powered media storage for creative teams", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Shade", + companyWebsite: "https://shade.inc/", + categories: ["AI/ML", "B2B", "Storage"], + overview: "Shade is a seed stage startup backed by General Catalyst, Contrary, SignalFire, and Bling building the future of file storage. AI-powered media storage solution that enables creative teams to instantly sync files from anywhere.", + founders: [ + { + id: "founder-61", + name: "Brandon Fan", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-62", + name: "Bright Xu", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-63", + name: "Edison Chiu", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-64", + name: "Gurish Sharma", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + investors: [ + { + id: "investor-16", + name: "General Catalyst", + type: "vc", + website: "https://www.generalcatalyst.com" + }, + { + id: "investor-17", + name: "Contrary Capital", + type: "vc", + website: "https://www.contrary.com" + }, + { + id: "investor-18", + name: "SignalFire", + type: "vc", + website: "https://www.signalfire.com" + }, + { + id: "investor-19", + name: "Bling", + type: "vc" + }, + { + id: "investor-20", + name: "GC Venture Fellows", + type: "vc" + }, + { + id: "investor-21", + name: "Gold House Ventures", + type: "vc" + }, + { + id: "investor-22", + name: "Z Fellows", + type: "accelerator" + } + ], + fundingStage: "seed", + sectionType: "funding", + sectionName: "General Catalyst", + sectionOrder: 4, + }, + { + id: "project-41", + title: "codefy.AI", + description: "Coding widget toolbox for developers", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "codefy.AI", + companyWebsite: "https://www.codefy.ai/", + categories: ["Developer Tools", "AI/ML", "Productivity"], + overview: "codefy.ai is your own toolbox of useful coding widgets to help you speed up your workflow. Write, explain, debug, compare, and translate code with 15+ helpful, customized tools.", + founders: [ + { + id: "founder-65", + name: "Cooper Saye", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-66", + name: "Brian Travis", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + fundingStage: "university", + sectionType: "cohort", + sectionName: "Fall 2024 Product Studio Cohort", + sectionOrder: 20, + }, + { + id: "project-42", + title: "wstwatch", + description: "Food waste tracking for dining halls", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "wstwatch", + companyWebsite: "", + categories: ["Sustainability", "B2B", "Food"], + overview: "wstwatch - CV food waste tracking for dining halls", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-43", + title: "frnds of frnds", + description: "Dating app based on mutual friends", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "frnds of frnds", + companyWebsite: "https://www.frndsoffrnds.com/", + categories: ["Social", "B2C", "Dating"], + overview: "LinkedIn meets Hinge: Frnds of Frnds is a dating app where your pool is mutual friends", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-44", + title: "Squad", + description: "Campus discovery platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Squad", + companyWebsite: "https://getsquad.app/", + categories: ["Community", "B2C", "Education"], + overview: "Squad is where campus comes together. Discover the best food, study spots, and more—all curated by students like you.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-45", + title: "Claris", + description: "Unified research workflow platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Claris", + companyWebsite: "https://claris-two.vercel.app/", + categories: ["Research", "B2B", "Productivity"], + overview: "Claris is a unified web platform that streamlines research workflows and save time across labs in any discipline.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-46", + title: "MBooking", + description: "Campus study space booking platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "MBooking", + companyWebsite: "https://mbooking.me", + categories: ["Productivity", "B2C", "Education"], + overview: "Find and book the perfect study space on campus without the hassle. Automated, simple, and built for students.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-47", + title: "Embedder.dev", + description: "AI tools for embedded systems engineers", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Embedder.dev", + companyWebsite: "embedder.dev", + categories: ["Developer Tools", "AI/ML", "Hardware"], + overview: "AI-native tools for embedded systems engineers", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-48", + title: "Dezu", + description: "AI tax accountant for high net worth clients", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Dezu", + companyWebsite: "https://dezu.ai/", + categories: ["AI/ML", "B2B", "Finance"], + overview: "Dezu is an AI tax accountan that automates the preparation of complex tax returns for high net worth clients.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-49", + title: "AgroQR", + description: "Environmental insights using QR sensors and drones", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "AgroQR", + companyWebsite: "https://www.useagroqr.com/", + categories: ["Agriculture", "B2B", "Sustainability"], + overview: "AgroQR delivers hyperlocal environmental insights using passive QR sensors and autonomous drones.", + founders: [], + investors: [ + { + id: "investor-31", + name: "UMich Center for Entrepreneurship", + type: "university-fund", + website: "https://cfe.umich.edu" + } + ], + fundingAmount: 5000, + fundingStage: "university", + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-50", + title: "Fathom", + description: "AI-powered runtime debugger", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Fathom", + companyWebsite: "", + categories: ["Developer Tools", "AI/ML", "Debugging"], + overview: "Fathom is an AI-powered debugger that seeks bugs during the runtime of programs while having access to its running context, memory, and stack trace.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-51", + title: "Form", + description: "Dance error detection using computer vision", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Form", + companyWebsite: "", + categories: ["AI/ML", "B2C", "Education"], + overview: "A computer vision model that maps data points to various body parts to dance videos and detects errors between a \"choreographer\" host video and a \"student\" client video.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-52", + title: "TagIt", + description: "Automated metadata tagging for sports photography", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "TagIt", + companyWebsite: "", + categories: ["AI/ML", "B2B", "Sports"], + overview: "TagIt automates metadata tagging in sports photography", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-53", + title: "TwinMind", + description: "AI sidekick for productivity", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "TwinMind", + companyWebsite: "https://twinmind.com/", + categories: ["AI/ML", "B2C", "Productivity"], + overview: "Your AI Sidekick Understands What You See and Hear to Boost Your Productivity", + founders: [], + investors: [ + { + id: "investor-23", + name: "Streamlined Ventures", + type: "vc" + }, + { + id: "investor-24", + name: "Sequoia Capital", + type: "vc", + website: "https://www.sequoiacap.com" + }, + { + id: "investor-25", + name: "Stephen Wolfram", + type: "angel" + } + ], + fundingAmount: 5700000, + fundingStage: "seed", + valuation: 60000000, + sectionType: "funding", + sectionName: "Sequoia Capital", + sectionOrder: 3, + }, + { + id: "project-54", + title: "Altrix", + description: "Nursing station automation", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Altrix", + companyWebsite: "https://www.altrix.tech/", + categories: ["Healthcare", "B2B", "AI/ML"], + overview: "Your Nursing Station On Auto-Pilot. Save nurses 3 hours a day on administrative tasks.", + founders: [ + { + id: "founder-67", + name: "Hector Benitez Ventura", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + investors: [ + { + id: "investor-28", + name: "Techstars Health AI Baltimore", + type: "accelerator", + website: "https://www.techstars.com" + }, + { + id: "investor-29", + name: "Armajaro Holdings", + type: "vc" + }, + { + id: "investor-30", + name: "Heliconia Capital", + type: "vc" + } + ], + fundingAmount: 300000, + fundingStage: "seed", + sectionType: "funding", + sectionName: "Techstars", + sectionOrder: 5, + }, + { + id: "project-55", + title: "RushLink", + description: "Live event experience for college campuses", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "RushLink", + companyWebsite: "https://download.therushlink.com", + categories: ["Events", "B2C", "Social"], + overview: "Bringing the Rush of Live Events to College Campuses", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-56", + title: "EarlyBird", + description: "Gen-Z marketing platform for new businesses", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "EarlyBird", + companyWebsite: "https://earlybirddrop.com", + categories: ["Marketing", "B2B", "SaaS"], + overview: "Gen-Z marketing platform for newly opened businesses", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-57", + title: "Nexxus Labs", + description: "Vehicle data network for urban mobility", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Nexxus Labs", + companyWebsite: "https://www.nexxuslabs.org/", + categories: ["Transportation", "B2B", "Data"], + overview: "Building a vehicle data network for the future of urban mobility", + founders: [ + { + id: "founder-68", + name: "Leo Gao", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-58", + title: "Oreen", + description: "Diagramming tool for ChatGPT", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Oreen", + companyWebsite: "https://oreen.dev", + categories: ["AI/ML", "B2C", "Productivity"], + overview: "The world's best diagramming tool for ChatGPT", + founders: [ + { + id: "founder-69", + name: "Benjamin Antonow", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-59", + title: "Scholarly", + description: "Context for research papers", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Scholarly", + companyWebsite: "", + categories: ["EdTech", "B2C", "Research"], + overview: "Scholarly provides fast, concise context for research papers. Scholarly reduces friction for people of any background trying to break into a field.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-60", + title: "Roots", + description: "Private social media for families", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Roots", + companyWebsite: "rootsapp.org", + categories: ["Social", "B2C", "Privacy"], + overview: "Roots is a private social media made specially for your family. In a world dominated by big tech selling your information, roots creates a privacy focused platform.", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-61", + title: "Temp8", + description: "Dance choreography app", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Temp8", + companyWebsite: "https://www.youtube.com/watch?v=TLt6yX44u1Q&t=1s", + categories: ["Education", "B2C", "Dance"], + overview: "An app that simplifies dance by breaking down choreography into 8-counts", + founders: [ + { + id: "founder-70", + name: "Maya Malik", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-71", + name: "Jeffery Hao Wu", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-62", + title: "Milieu", + description: "Skincare platform with science", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Milieu", + companyWebsite: "https://milieubio.com", + categories: ["Healthcare", "B2C", "Beauty"], + overview: "Dedicated to revolutionizing skincare through cutting-edge science and personalized solutions.", + founders: [ + { + id: "founder-72", + name: "Dev Kunjadia", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-63", + title: "0xKnowledge", + description: "Knowledge base management with AI", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "0xKnowledge", + companyWebsite: "https://0xknowledge.io/", + categories: ["AI/ML", "B2B", "Knowledge Management"], + overview: "0xKnowledge brings cutting edge technology to your fingertips through a knowledge base management system (KBMS) that is automatically maintained, and integrated with powerful AI tools", + founders: [ + { + id: "founder-73", + name: "Joshua Sum", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + { + id: "founder-74", + name: "Ammar Ateya", + role: "Founder", + imageSrc: "/placeholder.svg?height=150&width=150", + }, + ], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, + { + id: "project-64", + title: "Collage", + description: "AI academic advising platform", + imageSrc: "/placeholder.svg?height=200&width=300", + companyName: "Collage", + companyWebsite: "https://mycollage.us", + categories: ["EdTech", "AI/ML", "B2C"], + overview: "AI-powered academic advising and social scheduling platform for college students", + founders: [], + sectionType: "cohort", + sectionName: "Spring 2025 Product Studio Cohort", + sectionOrder: 21, + }, +] diff --git a/types/project.ts b/types/project.ts index 7a014e6..49bb3fd 100644 --- a/types/project.ts +++ b/types/project.ts @@ -1,5 +1,7 @@ export type SectionType = "funding" | "cohort" +export type InvestmentStage = "pre-seed" | "seed" | "series-a" | "accelerator" | "university" | "bootstrapped" + export interface Founder { id: string name: string @@ -7,6 +9,14 @@ export interface Founder { imageSrc: string } +export interface Investor { + id: string + name: string + type: "vc" | "accelerator" | "angel" | "university-fund" | "corporate" + website?: string + investmentStage?: string +} + export interface Project { id: string title: string @@ -17,6 +27,11 @@ export interface Project { categories: string[] overview: string founders: Founder[] + investors?: Investor[] + fundingAmount?: number + fundingStage?: InvestmentStage + valuation?: number + lastFundingDate?: string sectionType: SectionType sectionName: string sectionOrder: number From 0175eccfbfbf7fa69796ab01f37479b960e43e65 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Sat, 7 Feb 2026 12:15:27 -0500 Subject: [PATCH 10/53] feat: upgrade pnpm version --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a8770c3..5c167db 100644 --- a/package.json +++ b/package.json @@ -86,5 +86,6 @@ "postcss": "^8", "tailwindcss": "^3.4.17", "typescript": "^5" - } -} \ No newline at end of file + }, + "packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264" +} From e875cca6ac816e1bb8d96b458edbff23ebdbec3b Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 13:12:42 -0500 Subject: [PATCH 11/53] feat: pull project data from notion --- app/api/projects/route.ts | 21 +++++ .../components/FilterPanel.tsx | 8 +- .../components/ProjectCard.tsx | 14 +-- .../components/ProjectDirectoryLayout.tsx | 10 +-- .../components/ProjectList.tsx | 2 +- .../hooks/useProjectFilters.ts | 60 ++++++------- app/project-directory/page.tsx | 59 +++++++++---- app/store/layout.tsx | 1 + lib/notion.ts | 79 +++++++++++++++++ next.config.mjs | 4 + package.json | 1 + pnpm-lock.yaml | 37 ++++++++ types/notion.ts | 88 +++++++++++++++++++ types/project.ts | 14 ++- 14 files changed, 326 insertions(+), 72 deletions(-) create mode 100644 app/api/projects/route.ts create mode 100644 lib/notion.ts create mode 100644 types/notion.ts diff --git a/app/api/projects/route.ts b/app/api/projects/route.ts new file mode 100644 index 0000000..d150294 --- /dev/null +++ b/app/api/projects/route.ts @@ -0,0 +1,21 @@ +import { NextResponse } from "next/server" +import { Client } from "@notionhq/client" +import { transformNotionPageToProject } from "@/lib/notion" + +const notion = new Client({ auth: process.env.NOTION_API_KEY }) + +export async function GET() { + try { + const response = await notion.dataSources.query({ + data_source_id: process.env.NOTION_DATA_SOURCE_ID!, + }) + + const pages = response.results || [] + const projects = pages.map(transformNotionPageToProject) + + return NextResponse.json({ projects }) + } catch (error) { + console.error("Notion API error:", error) + return NextResponse.json({ error: "Failed to fetch projects from Notion" }, { status: 500 }) + } +} diff --git a/app/project-directory/components/FilterPanel.tsx b/app/project-directory/components/FilterPanel.tsx index 535692a..6a62129 100644 --- a/app/project-directory/components/FilterPanel.tsx +++ b/app/project-directory/components/FilterPanel.tsx @@ -14,10 +14,10 @@ interface FilterPanelProps { cohorts: string[] categories: string[] } - onSearchChange: (query: string) => void - onToggleFunding: (source: string) => void - onToggleCohort: (cohort: string) => void - onToggleCategory: (category: string) => void + onSearchChange: (_query: string) => void + onToggleFunding: (_source: string) => void + onToggleCohort: (_cohort: string) => void + onToggleCategory: (_category: string) => void onClearAll: () => void hasActiveFilters: boolean } diff --git a/app/project-directory/components/ProjectCard.tsx b/app/project-directory/components/ProjectCard.tsx index 8afa235..9ce5f02 100644 --- a/app/project-directory/components/ProjectCard.tsx +++ b/app/project-directory/components/ProjectCard.tsx @@ -134,12 +134,14 @@ export default function ProjectCard({ project, onClick }: ProjectCardProps) { key={founder.id} className="relative h-6 w-6 overflow-hidden rounded-full border-2 border-white bg-gray-100" > - {founder.name} + {founder.imageSrc && ( + {founder.name} + )}
))}
diff --git a/app/project-directory/components/ProjectDirectoryLayout.tsx b/app/project-directory/components/ProjectDirectoryLayout.tsx index 47733c2..1f061f9 100644 --- a/app/project-directory/components/ProjectDirectoryLayout.tsx +++ b/app/project-directory/components/ProjectDirectoryLayout.tsx @@ -18,13 +18,13 @@ interface ProjectDirectoryLayoutProps { cohorts: string[] categories: string[] } - onSearchChange: (query: string) => void - onToggleFunding: (source: string) => void - onToggleCohort: (cohort: string) => void - onToggleCategory: (category: string) => void + onSearchChange: (_query: string) => void + onToggleFunding: (_source: string) => void + onToggleCohort: (_cohort: string) => void + onToggleCategory: (_category: string) => void onClearAll: () => void hasActiveFilters: boolean - onProjectClick: (project: Project) => void + onProjectClick: (_project: Project) => void } export default function ProjectDirectoryLayout({ diff --git a/app/project-directory/components/ProjectList.tsx b/app/project-directory/components/ProjectList.tsx index c80e084..c1c8c01 100644 --- a/app/project-directory/components/ProjectList.tsx +++ b/app/project-directory/components/ProjectList.tsx @@ -4,7 +4,7 @@ import type { Project } from "@/types/project" interface ProjectListProps { projects: Project[] - onProjectClick: (project: Project) => void + onProjectClick: (_project: Project) => void } export default function ProjectList({ projects, onProjectClick }: ProjectListProps) { diff --git a/app/project-directory/hooks/useProjectFilters.ts b/app/project-directory/hooks/useProjectFilters.ts index 40ba806..a54da62 100644 --- a/app/project-directory/hooks/useProjectFilters.ts +++ b/app/project-directory/hooks/useProjectFilters.ts @@ -1,6 +1,5 @@ import { useState, useMemo } from "react" -import { projects } from "@/data/projects" -import type { Project, SectionType } from "@/types/project" +import type { Project } from "@/types/project" export interface FilterState { searchQuery: string @@ -9,7 +8,23 @@ export interface FilterState { categories: string[] } -export const useProjectFilters = () => { +export interface ProjectFiltersReturn { + filters: FilterState + filteredProjects: Project[] + filterOptions: { + fundingSources: string[] + cohorts: string[] + categories: string[] + } + setSearchQuery: (_query: string) => void + toggleFundingSource: (_source: string) => void + toggleCohort: (_cohort: string) => void + toggleCategory: (_category: string) => void + clearAllFilters: () => void + hasActiveFilters: boolean +} + +export const useProjectFilters = (projects: Project[] = []): ProjectFiltersReturn => { const [filters, setFilters] = useState({ searchQuery: "", fundingSources: [], @@ -17,69 +32,54 @@ export const useProjectFilters = () => { categories: [], }) - // Get all available filter options + // Filter options computed from projects const filterOptions = useMemo(() => { + if (!projects || projects.length === 0) return { fundingSources: [], cohorts: [], categories: [] } + const fundingSources = new Set() const cohorts = new Set() const categories = new Set() - - projects.forEach((project) => { - if (project.sectionType === "funding") { + + projects.forEach(project => { + if (project.sectionType === "funding" && project.sectionName) { fundingSources.add(project.sectionName) } else if (project.sectionType === "cohort") { cohorts.add(project.sectionName) } - project.categories.forEach((category) => categories.add(category)) + project.categories.forEach(cat => categories.add(cat)) }) - + return { fundingSources: Array.from(fundingSources).sort(), - cohorts: Array.from(cohorts).sort((a, b) => { - // Sort cohorts by recency (most recent first) - const cohortOrder = { - "Winter 2026 Product Studio Cohort": 23, - "Fall 2025 Product Studio Cohort": 22, - "Spring 2025 Product Studio Cohort": 21, - "Winter 2025 Product Studio Cohort": 21, - "Fall 2024 Product Studio Cohort": 20, - } - const orderA = cohortOrder[a as keyof typeof cohortOrder] || 0 - const orderB = cohortOrder[b as keyof typeof cohortOrder] || 0 - return orderB - orderA - }), + cohorts: Array.from(cohorts).sort(), categories: Array.from(categories).sort(), } - }, []) + }, [projects]) // Filter projects based on current filter state const filteredProjects = useMemo(() => { return projects.filter((project) => { - // Search filter const matchesSearch = filters.searchQuery === "" || project.title.toLowerCase().includes(filters.searchQuery.toLowerCase()) || project.companyName.toLowerCase().includes(filters.searchQuery.toLowerCase()) - // Funding source filter const matchesFunding = filters.fundingSources.length === 0 || (project.sectionType === "funding" && filters.fundingSources.includes(project.sectionName)) - // Cohort filter const matchesCohort = filters.cohorts.length === 0 || (project.sectionType === "cohort" && filters.cohorts.includes(project.sectionName)) - // Category filter const matchesCategory = filters.categories.length === 0 || project.categories.some((category) => filters.categories.includes(category)) return matchesSearch && matchesFunding && matchesCohort && matchesCategory }) - }, [filters]) + }, [filters, projects]) - // Update filter functions const updateFilters = (newFilters: Partial) => { setFilters((prev) => ({ ...prev, ...newFilters })) } @@ -138,4 +138,4 @@ export const useProjectFilters = () => { clearAllFilters, hasActiveFilters, } -} \ No newline at end of file +} diff --git a/app/project-directory/page.tsx b/app/project-directory/page.tsx index cdee3a0..a9bcf4b 100644 --- a/app/project-directory/page.tsx +++ b/app/project-directory/page.tsx @@ -1,14 +1,16 @@ -"use client" +"use client"; -import { useState } from "react" +import { useState, useEffect } from "react" import Header from "@/components/header" import Footer from "@/components/footer" import ProjectModal from "@/components/project-modal" import ProjectDirectoryLayout from "./components/ProjectDirectoryLayout" -import { useProjectFilters } from "./hooks/useProjectFilters" import type { Project } from "@/types/project" +import { useProjectFilters } from "./hooks/useProjectFilters" export default function ProjectDirectoryPage() { + const [projects, setProjects] = useState([]) + const [isLoading, setIsLoading] = useState(true) const [selectedProject, setSelectedProject] = useState(null) const [isModalOpen, setIsModalOpen] = useState(false) @@ -22,7 +24,23 @@ export default function ProjectDirectoryPage() { toggleCategory, clearAllFilters, hasActiveFilters, - } = useProjectFilters() + } = useProjectFilters(projects) + + useEffect(() => { + const fetchProjects = async () => { + try { + const response = await fetch("/api/projects") + const data = await response.json() + setProjects(data.projects || []) + } catch (error) { + console.error("Failed to fetch projects:", error) + } finally { + setIsLoading(false) + } + } + + fetchProjects() + }, []) const openProjectModal = (project: Project) => { setSelectedProject(project) @@ -31,6 +49,7 @@ export default function ProjectDirectoryPage() { const closeProjectModal = () => { setIsModalOpen(false) + setTimeout(() => setSelectedProject(null), 200) } return ( @@ -46,19 +65,25 @@ export default function ProjectDirectoryPage() {

- {/* Main Layout */} - + {isLoading ? ( +
+
+
+ ) : ( + /* Main Layout */ + + )} diff --git a/app/store/layout.tsx b/app/store/layout.tsx index c6c7f13..cb8df98 100644 --- a/app/store/layout.tsx +++ b/app/store/layout.tsx @@ -1,3 +1,4 @@ +import React from "react" import { CartProvider } from "@/components/store/cart-provider"; import { CartSheet } from "@/components/store/cart-sheet"; diff --git a/lib/notion.ts b/lib/notion.ts new file mode 100644 index 0000000..63c9f95 --- /dev/null +++ b/lib/notion.ts @@ -0,0 +1,79 @@ +import type { Project } from "@/types/project" + +export function extractTitleInfo(titleProp: any): { name: string; link: string | null } { + const titleText = titleProp.title?.[0]?.text + if (!titleText) { + return { name: "", link: null } + } + return { + name: titleText.content || "", + link: titleText.link?.url || null + } +} + +export function extractPlainText(richTextProp: any): string { + return richTextProp.rich_text?.[0]?.plain_text || "" +} + +export function extractMultiSelect(multiSelectProp: any): string[] { + return multiSelectProp.multi_select?.map((item: any) => item.name) || [] +} + +export function parseInvestorName(investorString: string): { name: string; type: string } { + const match = investorString.match(/^(.+?)\s*\((.+)\)$/) + if (match) { + return { name: match[1].trim(), type: match[2].trim() } + } + return { name: investorString, type: "unknown" } +} + +export function parseCohortName(cohortName: string): string { + return cohortName.replace(" Product Studio", "") +} + +export function getCohortOrder(cohortName: string): number { + const orderMap: Record = { + "Winter 2026": 23, + "Fall 2025": 22, + "Winter 2025": 21, + "Fall 2024": 20, + "Winter 2024": 19, + } + return orderMap[cohortName] || 0 +} + +export function generatePlaceholderImage(_companyName: string, size: string = "64x64"): string { + return `https://placehold.co/${size}.jpg` +} + +export function transformNotionPageToProject(page: any): Project { + const { id, properties } = page + const nameInfo = extractTitleInfo(properties.name) + const hasInvestors = properties.investors?.multi_select?.length > 0 + + return { + id, + title: nameInfo.name, + description: extractPlainText(properties.description), + overview: extractPlainText(properties.overview), + imageSrc: generatePlaceholderImage(nameInfo.name, "64x64"), + companyName: nameInfo.name, + companyWebsite: properties.website?.url || nameInfo.link || null, + categories: extractMultiSelect(properties.categories), + founders: properties.founders?.multi_select?.map((f: any) => ({ + id: f.id, + name: f.name, + role: "Founder", + imageSrc: generatePlaceholderImage(f.name, "24x24"), + })) || [], + investors: hasInvestors ? properties.investors.multi_select?.map((inv: any) => ({ + id: inv.id, + name: parseInvestorName(inv.name).name, + type: parseInvestorName(inv.name).type, + website: null, + })) : undefined, + sectionType: hasInvestors ? "funding" : "cohort", + sectionName: hasInvestors ? properties.investors.multi_select[0].name : properties.cohort?.select?.name || "", + sectionOrder: getCohortOrder(properties.cohort?.select?.name || ""), + } +} diff --git a/next.config.mjs b/next.config.mjs index eb96ecc..336d714 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -24,6 +24,10 @@ const nextConfig = { protocol: 'https', hostname: 'xxlvdpgngmcvkzcsatuz.supabase.co', }, + { + protocol: 'https', + hostname: 'placehold.co', + }, ], }, experimental: { diff --git a/package.json b/package.json index 5c167db..d56633d 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.9.1", + "@notionhq/client": "^5.9.0", "@posthog/nextjs": "^0.0.2", "@radix-ui/react-accordion": "^1.2.2", "@radix-ui/react-alert-dialog": "^1.1.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24509b9..e3e1f04 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@hookform/resolvers': specifier: ^3.9.1 version: 3.10.0(react-hook-form@7.60.0(react@19.2.3)) + '@notionhq/client': + specifier: ^5.9.0 + version: 5.9.0 '@posthog/nextjs': specifier: ^0.0.2 version: 0.0.2(next@16.1.0(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) @@ -428,89 +431,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -601,24 +620,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@16.1.0': resolution: {integrity: sha512-TojQnDRoX7wJWXEEwdfuJtakMDW64Q7NrxQPviUnfYJvAx5/5wcGE+1vZzQ9F17m+SdpFeeXuOr6v3jbyusYMQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@16.1.0': resolution: {integrity: sha512-quhNFVySW4QwXiZkZ34SbfzNBm27vLrxZ2HwTfFFO1BBP0OY1+pI0nbyewKeq1FriqU+LZrob/cm26lwsiAi8Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@16.1.0': resolution: {integrity: sha512-6JW0z2FZUK5iOVhUIWqE4RblAhUj1EwhZ/MwteGb//SpFTOHydnhbp3868gxalwea+mbOLWO6xgxj9wA9wNvNw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@16.1.0': resolution: {integrity: sha512-+DK/akkAvvXn5RdYN84IOmLkSy87SCmpofJPdB8vbLmf01BzntPBSYXnMvnEEv/Vcf3HYJwt24QZ/s6sWAwOMQ==} @@ -648,6 +671,10 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@notionhq/client@5.9.0': + resolution: {integrity: sha512-TvAVMfwtVv61hsPrRfB9ehgzSjX6DaAi1ZRAnpg8xFjzaXhzhEfbO0PhBRm3ecSv1azDuO2kBuyQHh2/z7G4YQ==} + engines: {node: '>=18'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1546,41 +1573,49 @@ packages: resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] + libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] + libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.11.1': resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] + libc: [musl] '@unrs/resolver-binding-wasm32-wasi@1.11.1': resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} @@ -4140,6 +4175,8 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@notionhq/client@5.9.0': {} + '@pkgjs/parseargs@0.11.0': optional: true diff --git a/types/notion.ts b/types/notion.ts new file mode 100644 index 0000000..db5b210 --- /dev/null +++ b/types/notion.ts @@ -0,0 +1,88 @@ +export interface NotionPage { + object: "page" + id: string + created_time: string + last_edited_time: string + created_by: { + object: "user" + id: string + } + last_edited_by: { + object: "user" + id: string + } + cover: string | null + icon: string | null + parent: { + type: "data_source_id" + data_source_id: string + database_id: string + } + archived: boolean + in_trash: boolean + is_locked: boolean + properties: { + Name: NotionTitle + description: NotionRichText + overview: NotionRichText + website: NotionUrl + categories: NotionMultiSelect + founders: NotionMultiSelect + investors: NotionMultiSelect + cohort: NotionSelect + contacts: NotionMultiSelect + } +} + +export interface NotionTitle { + id: "title" + type: "title" + title: { + type: "text" + text: { + content: string + link?: { + url: string + } + annotations: Record + } + } +} + +export interface NotionRichText { + id: string + type: "rich_text" + rich_text: { + type: "text" + text: { + content: string + annotations: Record + } + } +} + +export interface NotionUrl { + id: "url" + type: "url" + url: string | null +} + +export interface NotionMultiSelect { + id: "multi_select" + type: "multi_select" + multi_select: { + id: string + name: string + color: string + } +} + +export interface NotionSelect { + id: "select" + type: "select" + select: { + id: string + name: string + color: string + } +} diff --git a/types/project.ts b/types/project.ts index 49bb3fd..d070361 100644 --- a/types/project.ts +++ b/types/project.ts @@ -5,7 +5,7 @@ export type InvestmentStage = "pre-seed" | "seed" | "series-a" | "accelerator" | export interface Founder { id: string name: string - role: string + role: "Founder" imageSrc: string } @@ -13,26 +13,22 @@ export interface Investor { id: string name: string type: "vc" | "accelerator" | "angel" | "university-fund" | "corporate" - website?: string - investmentStage?: string + website: string | null } export interface Project { id: string title: string description: string + overview: string imageSrc: string companyName: string - companyWebsite: string + companyWebsite: string | null categories: string[] overview: string founders: Founder[] investors?: Investor[] - fundingAmount?: number - fundingStage?: InvestmentStage - valuation?: number - lastFundingDate?: string - sectionType: SectionType + sectionType: "funding" | "cohort" sectionName: string sectionOrder: number } From 7f65b1b2459c5b548c7b57e4bcd7413bb94055c1 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 13:22:26 -0500 Subject: [PATCH 12/53] refactor: reorder cohorts and prioritize funded projects --- app/api/projects/route.ts | 5 +++-- lib/notion.ts | 27 ++++++++++++++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/app/api/projects/route.ts b/app/api/projects/route.ts index d150294..f413b55 100644 --- a/app/api/projects/route.ts +++ b/app/api/projects/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from "next/server" import { Client } from "@notionhq/client" -import { transformNotionPageToProject } from "@/lib/notion" +import { transformNotionPageToProject, sortProjects } from "@/lib/notion" const notion = new Client({ auth: process.env.NOTION_API_KEY }) @@ -12,8 +12,9 @@ export async function GET() { const pages = response.results || [] const projects = pages.map(transformNotionPageToProject) + const sortedProjects = sortProjects(projects) - return NextResponse.json({ projects }) + return NextResponse.json({ projects: sortedProjects }) } catch (error) { console.error("Notion API error:", error) return NextResponse.json({ error: "Failed to fetch projects from Notion" }, { status: 500 }) diff --git a/lib/notion.ts b/lib/notion.ts index 63c9f95..6e722b9 100644 --- a/lib/notion.ts +++ b/lib/notion.ts @@ -32,14 +32,27 @@ export function parseCohortName(cohortName: string): string { } export function getCohortOrder(cohortName: string): number { - const orderMap: Record = { - "Winter 2026": 23, - "Fall 2025": 22, - "Winter 2025": 21, - "Fall 2024": 20, - "Winter 2024": 19, + const match = cohortName.match(/(Winter|Fall)\s+(\d{4})/i) + if (!match) return 0 + + const season = match[1] + const year = parseInt(match[2]) + + const seasonPriority: Record = { + "Winter": 2, + "Fall": 1 } - return orderMap[cohortName] || 0 + + return year * 10 + seasonPriority[season] +} + +export function sortProjects(projects: Project[]): Project[] { + return [...projects].sort((a, b) => { + if (a.sectionType !== b.sectionType) { + return a.sectionType === "funding" ? -1 : 1 + } + return b.sectionOrder - a.sectionOrder + }) } export function generatePlaceholderImage(_companyName: string, size: string = "64x64"): string { From 64d1340f3974449d7c319348a365c9cd0f21221b Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 13:30:55 -0500 Subject: [PATCH 13/53] refactor: server side filtering for projects --- app/api/projects/route.ts | 48 +++++- .../components/ProjectList.tsx | 23 +-- .../hooks/useProjectFilters.ts | 141 ------------------ app/project-directory/page.tsx | 140 ++++++++++++++--- lib/notion.ts | 50 +++++++ 5 files changed, 216 insertions(+), 186 deletions(-) delete mode 100644 app/project-directory/hooks/useProjectFilters.ts diff --git a/app/api/projects/route.ts b/app/api/projects/route.ts index f413b55..005a5c4 100644 --- a/app/api/projects/route.ts +++ b/app/api/projects/route.ts @@ -1,22 +1,56 @@ import { NextResponse } from "next/server" import { Client } from "@notionhq/client" -import { transformNotionPageToProject, sortProjects } from "@/lib/notion" +import { transformNotionPageToProject, sortProjects, extractFilterOptions, filterProjects } from "@/lib/notion" const notion = new Client({ auth: process.env.NOTION_API_KEY }) -export async function GET() { +export async function GET(request: Request) { try { + const { searchParams } = new URL(request.url) + + // Parse filter parameters + const searchQuery = searchParams.get("search") || "" + const fundingSources = searchParams.getAll("funding") + const cohorts = searchParams.getAll("cohort") + const categories = searchParams.getAll("category") + const response = await notion.dataSources.query({ data_source_id: process.env.NOTION_DATA_SOURCE_ID!, }) const pages = response.results || [] - const projects = pages.map(transformNotionPageToProject) - const sortedProjects = sortProjects(projects) - - return NextResponse.json({ projects: sortedProjects }) + const allProjects = pages.map(transformNotionPageToProject) + + // Sort all projects first + const sortedProjects = sortProjects(allProjects) + + // Extract filter options from ALL projects (not filtered) + const filterOptions = extractFilterOptions(sortedProjects) + + // Apply filters + const filteredProjects = filterProjects(sortedProjects, { + searchQuery, + fundingSources, + cohorts, + categories, + }) + + return NextResponse.json({ + projects: filteredProjects, + filterOptions, + totalProjects: allProjects.length, + filteredCount: filteredProjects.length + }, { + headers: { + 'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300' + } + }) } catch (error) { console.error("Notion API error:", error) - return NextResponse.json({ error: "Failed to fetch projects from Notion" }, { status: 500 }) + return NextResponse.json({ + error: "Failed to fetch projects from Notion" + }, { + status: 500 + }) } } diff --git a/app/project-directory/components/ProjectList.tsx b/app/project-directory/components/ProjectList.tsx index c1c8c01..bdb3160 100644 --- a/app/project-directory/components/ProjectList.tsx +++ b/app/project-directory/components/ProjectList.tsx @@ -1,4 +1,3 @@ -import { useMemo } from "react" import ProjectCard from "./ProjectCard" import type { Project } from "@/types/project" @@ -8,24 +7,6 @@ interface ProjectListProps { } export default function ProjectList({ projects, onProjectClick }: ProjectListProps) { - // Sort projects: funding projects first, then cohorts, maintaining existing order logic - const sortedProjects = useMemo(() => { - return [...projects].sort((a, b) => { - // If both are same type, sort by section order - if (a.sectionType === b.sectionType) { - if (a.sectionType === "funding") { - // For funding, lower order = higher priority - return a.sectionOrder - b.sectionOrder - } else { - // For cohorts, higher order = more recent - return b.sectionOrder - a.sectionOrder - } - } - // Funding projects come first - return a.sectionType === "funding" ? -1 : 1 - }) - }, [projects]) - if (projects.length === 0) { return (
@@ -41,7 +22,7 @@ export default function ProjectList({ projects, onProjectClick }: ProjectListPro return (
- {sortedProjects.map((project) => ( + {projects.map((project) => ( ) -} \ No newline at end of file +} diff --git a/app/project-directory/hooks/useProjectFilters.ts b/app/project-directory/hooks/useProjectFilters.ts deleted file mode 100644 index a54da62..0000000 --- a/app/project-directory/hooks/useProjectFilters.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { useState, useMemo } from "react" -import type { Project } from "@/types/project" - -export interface FilterState { - searchQuery: string - fundingSources: string[] - cohorts: string[] - categories: string[] -} - -export interface ProjectFiltersReturn { - filters: FilterState - filteredProjects: Project[] - filterOptions: { - fundingSources: string[] - cohorts: string[] - categories: string[] - } - setSearchQuery: (_query: string) => void - toggleFundingSource: (_source: string) => void - toggleCohort: (_cohort: string) => void - toggleCategory: (_category: string) => void - clearAllFilters: () => void - hasActiveFilters: boolean -} - -export const useProjectFilters = (projects: Project[] = []): ProjectFiltersReturn => { - const [filters, setFilters] = useState({ - searchQuery: "", - fundingSources: [], - cohorts: [], - categories: [], - }) - - // Filter options computed from projects - const filterOptions = useMemo(() => { - if (!projects || projects.length === 0) return { fundingSources: [], cohorts: [], categories: [] } - - const fundingSources = new Set() - const cohorts = new Set() - const categories = new Set() - - projects.forEach(project => { - if (project.sectionType === "funding" && project.sectionName) { - fundingSources.add(project.sectionName) - } else if (project.sectionType === "cohort") { - cohorts.add(project.sectionName) - } - project.categories.forEach(cat => categories.add(cat)) - }) - - return { - fundingSources: Array.from(fundingSources).sort(), - cohorts: Array.from(cohorts).sort(), - categories: Array.from(categories).sort(), - } - }, [projects]) - - // Filter projects based on current filter state - const filteredProjects = useMemo(() => { - return projects.filter((project) => { - const matchesSearch = - filters.searchQuery === "" || - project.title.toLowerCase().includes(filters.searchQuery.toLowerCase()) || - project.companyName.toLowerCase().includes(filters.searchQuery.toLowerCase()) - - const matchesFunding = - filters.fundingSources.length === 0 || - (project.sectionType === "funding" && filters.fundingSources.includes(project.sectionName)) - - const matchesCohort = - filters.cohorts.length === 0 || - (project.sectionType === "cohort" && filters.cohorts.includes(project.sectionName)) - - const matchesCategory = - filters.categories.length === 0 || - project.categories.some((category) => filters.categories.includes(category)) - - return matchesSearch && matchesFunding && matchesCohort && matchesCategory - }) - }, [filters, projects]) - - const updateFilters = (newFilters: Partial) => { - setFilters((prev) => ({ ...prev, ...newFilters })) - } - - const setSearchQuery = (query: string) => { - updateFilters({ searchQuery: query }) - } - - const toggleFundingSource = (source: string) => { - const current = filters.fundingSources - const updated = current.includes(source) - ? current.filter((s) => s !== source) - : [...current, source] - updateFilters({ fundingSources: updated }) - } - - const toggleCohort = (cohort: string) => { - const current = filters.cohorts - const updated = current.includes(cohort) - ? current.filter((c) => c !== cohort) - : [...current, cohort] - updateFilters({ cohorts: updated }) - } - - const toggleCategory = (category: string) => { - const current = filters.categories - const updated = current.includes(category) - ? current.filter((c) => c !== category) - : [...current, category] - updateFilters({ categories: updated }) - } - - const clearAllFilters = () => { - setFilters({ - searchQuery: "", - fundingSources: [], - cohorts: [], - categories: [], - }) - } - - const hasActiveFilters = - filters.searchQuery !== "" || - filters.fundingSources.length > 0 || - filters.cohorts.length > 0 || - filters.categories.length > 0 - - return { - filters, - filteredProjects, - filterOptions, - setSearchQuery, - toggleFundingSource, - toggleCohort, - toggleCategory, - clearAllFilters, - hasActiveFilters, - } -} diff --git a/app/project-directory/page.tsx b/app/project-directory/page.tsx index a9bcf4b..c53ee6c 100644 --- a/app/project-directory/page.tsx +++ b/app/project-directory/page.tsx @@ -1,46 +1,139 @@ "use client"; import { useState, useEffect } from "react" +import { useSearchParams, useRouter } from "next/navigation" import Header from "@/components/header" import Footer from "@/components/footer" import ProjectModal from "@/components/project-modal" import ProjectDirectoryLayout from "./components/ProjectDirectoryLayout" import type { Project } from "@/types/project" -import { useProjectFilters } from "./hooks/useProjectFilters" export default function ProjectDirectoryPage() { + const searchParams = useSearchParams() + const router = useRouter() const [projects, setProjects] = useState([]) + const [filterOptions, setFilterOptions] = useState<{ + fundingSources: string[] + cohorts: string[] + categories: string[] + }>({ fundingSources: [], cohorts: [], categories: [] }) const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) const [selectedProject, setSelectedProject] = useState(null) const [isModalOpen, setIsModalOpen] = useState(false) - const { - filters, - filteredProjects, - filterOptions, - setSearchQuery, - toggleFundingSource, - toggleCohort, - toggleCategory, - clearAllFilters, - hasActiveFilters, - } = useProjectFilters(projects) + // Get current filters from URL + const filters = { + searchQuery: searchParams?.get("search") || "", + fundingSources: searchParams?.getAll("funding") || [], + cohorts: searchParams?.getAll("cohort") || [], + categories: searchParams?.getAll("category") || [], + } + + const hasActiveFilters = + filters.searchQuery !== "" || + filters.fundingSources.length > 0 || + filters.cohorts.length > 0 || + filters.categories.length > 0 + // Fetch projects when filters change useEffect(() => { const fetchProjects = async () => { + setIsLoading(true) + setError(null) try { - const response = await fetch("/api/projects") + const params = new URLSearchParams() + if (filters.searchQuery) params.set("search", filters.searchQuery) + filters.fundingSources.forEach(f => params.append("funding", f)) + filters.cohorts.forEach(c => params.append("cohort", c)) + filters.categories.forEach(c => params.append("category", c)) + + const response = await fetch(`/api/projects?${params.toString()}`) + + if (!response.ok) { + throw new Error("Failed to fetch projects") + } + const data = await response.json() setProjects(data.projects || []) + setFilterOptions(data.filterOptions || { fundingSources: [], cohorts: [], categories: [] }) } catch (error) { console.error("Failed to fetch projects:", error) + setError("Unable to load projects. Please try again later.") } finally { setIsLoading(false) } } fetchProjects() - }, []) + }, [searchParams]) + + // Update URL when filters change + const updateFilters = (updates: { + searchQuery?: string + fundingSources?: string[] + cohorts?: string[] + categories?: string[] + }) => { + const params = new URLSearchParams(searchParams?.toString() || "") + + if (updates.searchQuery !== undefined) { + if (updates.searchQuery) { + params.set("search", updates.searchQuery) + } else { + params.delete("search") + } + } + + if (updates.fundingSources !== undefined) { + params.delete("funding") + updates.fundingSources.forEach(f => params.append("funding", f)) + } + + if (updates.cohorts !== undefined) { + params.delete("cohort") + updates.cohorts.forEach(c => params.append("cohort", c)) + } + + if (updates.categories !== undefined) { + params.delete("category") + updates.categories.forEach(c => params.append("category", c)) + } + + router.push(`?${params.toString()}`, { scroll: false }) + } + + const setSearchQuery = (query: string) => { + updateFilters({ searchQuery: query }) + } + + const toggleFundingSource = (source: string) => { + const current = filters.fundingSources + const updated = current.includes(source) + ? current.filter((s) => s !== source) + : [...current, source] + updateFilters({ fundingSources: updated }) + } + + const toggleCohort = (cohort: string) => { + const current = filters.cohorts + const updated = current.includes(cohort) + ? current.filter((c) => c !== cohort) + : [...current, cohort] + updateFilters({ cohorts: updated }) + } + + const toggleCategory = (category: string) => { + const current = filters.categories + const updated = current.includes(category) + ? current.filter((c) => c !== category) + : [...current, category] + updateFilters({ categories: updated }) + } + + const clearAllFilters = () => { + router.push("/project-directory", { scroll: false }) + } const openProjectModal = (project: Project) => { setSelectedProject(project) @@ -61,18 +154,31 @@ export default function ProjectDirectoryPage() {

Project Directory

- A curated showcase of innovative startups and products built by founders and teams from the V1 ecosystem. + A curated showcase of innovative startups and products built by founders and teams from V1 ecosystem.

- {isLoading ? ( + {error ? ( +
+
+

Error

+

{error}

+ +
+
+ ) : isLoading ? (
) : ( /* Main Layout */ () + const cohorts = new Set() + const categories = new Set() + + projects.forEach(project => { + if (project.sectionType === "funding" && project.sectionName) { + fundingSources.add(project.sectionName) + } else if (project.sectionType === "cohort") { + cohorts.add(project.sectionName) + } + project.categories.forEach(cat => categories.add(cat)) + }) + + return { + fundingSources: Array.from(fundingSources).sort(), + cohorts: Array.from(cohorts).sort(), + categories: Array.from(categories).sort(), + } +} + +export function filterProjects(projects: Project[], filters: { + searchQuery?: string + fundingSources?: string[] + cohorts?: string[] + categories?: string[] +}) { + return projects.filter((project) => { + const searchQuery = filters.searchQuery?.toLowerCase() || "" + const matchesSearch = + searchQuery === "" || + project.title.toLowerCase().includes(searchQuery) || + project.companyName.toLowerCase().includes(searchQuery) + + const matchesFunding = + !filters.fundingSources?.length || + (project.sectionType === "funding" && filters.fundingSources.includes(project.sectionName)) + + const matchesCohort = + !filters.cohorts?.length || + (project.sectionType === "cohort" && filters.cohorts.includes(project.sectionName)) + + const matchesCategory = + !filters.categories?.length || + project.categories.some((category) => filters.categories!.includes(category)) + + return matchesSearch && matchesFunding && matchesCohort && matchesCategory + }) +} + export function generatePlaceholderImage(_companyName: string, size: string = "64x64"): string { return `https://placehold.co/${size}.jpg` } From 1fd6626cde7b62f95338b1f87b5584fc9f84fab5 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 13:39:42 -0500 Subject: [PATCH 14/53] feat: load page skeleton and then data --- .../components/ProjectDirectoryLayout.tsx | 63 ++++++++++++------- app/project-directory/page.tsx | 7 +-- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/app/project-directory/components/ProjectDirectoryLayout.tsx b/app/project-directory/components/ProjectDirectoryLayout.tsx index 1f061f9..f18380c 100644 --- a/app/project-directory/components/ProjectDirectoryLayout.tsx +++ b/app/project-directory/components/ProjectDirectoryLayout.tsx @@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button" import { Filter } from "lucide-react" import type { Project } from "@/types/project" -interface ProjectDirectoryLayoutProps { + interface ProjectDirectoryLayoutProps { projects: Project[] filters: { searchQuery: string @@ -25,20 +25,22 @@ interface ProjectDirectoryLayoutProps { onClearAll: () => void hasActiveFilters: boolean onProjectClick: (_project: Project) => void + isLoading?: boolean } -export default function ProjectDirectoryLayout({ - projects, - filters, - filterOptions, - onSearchChange, - onToggleFunding, - onToggleCohort, - onToggleCategory, - onClearAll, - hasActiveFilters, - onProjectClick, -}: ProjectDirectoryLayoutProps) { + export default function ProjectDirectoryLayout({ + projects, + filters, + filterOptions, + onSearchChange, + onToggleFunding, + onToggleCohort, + onToggleCategory, + onClearAll, + hasActiveFilters, + onProjectClick, + isLoading = false, + }: ProjectDirectoryLayoutProps) { const [isMobileFilterOpen, setIsMobileFilterOpen] = useState(false) return ( @@ -77,8 +79,14 @@ export default function ProjectDirectoryLayout({ Clear filters )} -
- +
+ {isLoading ? ( +
+
+
+ ) : ( + + )}
@@ -95,12 +103,13 @@ export default function ProjectDirectoryLayout({ className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" /> - - ) : isLoading ? ( -
-
-
- ) : ( + ) : ( /* Main Layout */ )} From 7ad63840c5af207a20f3057011a3e5b88e4e79ec Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 13:45:21 -0500 Subject: [PATCH 15/53] refactor: remove clear all filters button --- .../components/FilterPanel.tsx | 20 +------ .../components/ProjectDirectoryLayout.tsx | 54 ++++++++----------- app/project-directory/page.tsx | 12 +---- 3 files changed, 24 insertions(+), 62 deletions(-) diff --git a/app/project-directory/components/FilterPanel.tsx b/app/project-directory/components/FilterPanel.tsx index 6a62129..2b8e964 100644 --- a/app/project-directory/components/FilterPanel.tsx +++ b/app/project-directory/components/FilterPanel.tsx @@ -1,6 +1,5 @@ import { Input } from "@/components/ui/input" -import { Button } from "@/components/ui/button" -import { X, Search, Building, Users, Tag } from "lucide-react" +import { Search, Building, Users, Tag } from "lucide-react" interface FilterPanelProps { filters: { @@ -18,8 +17,6 @@ interface FilterPanelProps { onToggleFunding: (_source: string) => void onToggleCohort: (_cohort: string) => void onToggleCategory: (_category: string) => void - onClearAll: () => void - hasActiveFilters: boolean } export default function FilterPanel({ @@ -29,25 +26,12 @@ export default function FilterPanel({ onToggleFunding, onToggleCohort, onToggleCategory, - onClearAll, - hasActiveFilters, }: FilterPanelProps) { return (
- {/* Header */} + {/* Header */}

Filters

- {hasActiveFilters && ( - - )}
{/* Search */} diff --git a/app/project-directory/components/ProjectDirectoryLayout.tsx b/app/project-directory/components/ProjectDirectoryLayout.tsx index f18380c..632be7b 100644 --- a/app/project-directory/components/ProjectDirectoryLayout.tsx +++ b/app/project-directory/components/ProjectDirectoryLayout.tsx @@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button" import { Filter } from "lucide-react" import type { Project } from "@/types/project" - interface ProjectDirectoryLayoutProps { +interface ProjectDirectoryLayoutProps { projects: Project[] filters: { searchQuery: string @@ -22,8 +22,6 @@ import type { Project } from "@/types/project" onToggleFunding: (_source: string) => void onToggleCohort: (_cohort: string) => void onToggleCategory: (_category: string) => void - onClearAll: () => void - hasActiveFilters: boolean onProjectClick: (_project: Project) => void isLoading?: boolean } @@ -36,20 +34,24 @@ import type { Project } from "@/types/project" onToggleFunding, onToggleCohort, onToggleCategory, - onClearAll, - hasActiveFilters, onProjectClick, isLoading = false, }: ProjectDirectoryLayoutProps) { const [isMobileFilterOpen, setIsMobileFilterOpen] = useState(false) + const hasActiveFilters = + filters.searchQuery !== "" || + filters.fundingSources.length > 0 || + filters.cohorts.length > 0 || + filters.categories.length > 0 + return ( <> {/* Desktop Layout */}
{/* Filter Panel - Left Side */}
-
+
- {/* Project List - Right Side */} + {/* Project List - Right Side */}

Projects ({projects.length})

- {hasActiveFilters && ( - - )} -
+
{isLoading ? (
@@ -159,19 +149,17 @@ import type { Project } from "@/types/project"
- {/* Filter Content */} -
- -
+ {/* Filter Content */} +
+ +
{/* Footer */}
diff --git a/app/project-directory/page.tsx b/app/project-directory/page.tsx index 72745ee..ae60e58 100644 --- a/app/project-directory/page.tsx +++ b/app/project-directory/page.tsx @@ -30,11 +30,7 @@ export default function ProjectDirectoryPage() { categories: searchParams?.getAll("category") || [], } - const hasActiveFilters = - filters.searchQuery !== "" || - filters.fundingSources.length > 0 || - filters.cohorts.length > 0 || - filters.categories.length > 0 + // Fetch projects when filters change // Fetch projects when filters change useEffect(() => { @@ -131,10 +127,6 @@ export default function ProjectDirectoryPage() { updateFilters({ categories: updated }) } - const clearAllFilters = () => { - router.push("/project-directory", { scroll: false }) - } - const openProjectModal = (project: Project) => { setSelectedProject(project) setIsModalOpen(true) @@ -181,8 +173,6 @@ export default function ProjectDirectoryPage() { onToggleFunding={toggleFundingSource} onToggleCohort={toggleCohort} onToggleCategory={toggleCategory} - onClearAll={clearAllFilters} - hasActiveFilters={hasActiveFilters} onProjectClick={openProjectModal} isLoading={isLoading} /> From 1ba1a181666dba385f645d4b57a9c7f2fcced11b Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 13:55:43 -0500 Subject: [PATCH 16/53] refactor: use tanstack query --- app/project-directory/page.tsx | 47 +++---------------------- hooks/useProjects.ts | 63 ++++++++++++++++++++++++++++++++++ types/project.ts | 1 - 3 files changed, 67 insertions(+), 44 deletions(-) create mode 100644 hooks/useProjects.ts diff --git a/app/project-directory/page.tsx b/app/project-directory/page.tsx index ae60e58..2967103 100644 --- a/app/project-directory/page.tsx +++ b/app/project-directory/page.tsx @@ -1,24 +1,17 @@ "use client"; -import { useState, useEffect } from "react" +import { useState } from "react" import { useSearchParams, useRouter } from "next/navigation" import Header from "@/components/header" import Footer from "@/components/footer" import ProjectModal from "@/components/project-modal" import ProjectDirectoryLayout from "./components/ProjectDirectoryLayout" import type { Project } from "@/types/project" +import { useProjects } from "@/hooks/useProjects" export default function ProjectDirectoryPage() { const searchParams = useSearchParams() const router = useRouter() - const [projects, setProjects] = useState([]) - const [filterOptions, setFilterOptions] = useState<{ - fundingSources: string[] - cohorts: string[] - categories: string[] - }>({ fundingSources: [], cohorts: [], categories: [] }) - const [isLoading, setIsLoading] = useState(true) - const [error, setError] = useState(null) const [selectedProject, setSelectedProject] = useState(null) const [isModalOpen, setIsModalOpen] = useState(false) @@ -30,39 +23,7 @@ export default function ProjectDirectoryPage() { categories: searchParams?.getAll("category") || [], } - // Fetch projects when filters change - - // Fetch projects when filters change - useEffect(() => { - const fetchProjects = async () => { - setIsLoading(true) - setError(null) - try { - const params = new URLSearchParams() - if (filters.searchQuery) params.set("search", filters.searchQuery) - filters.fundingSources.forEach(f => params.append("funding", f)) - filters.cohorts.forEach(c => params.append("cohort", c)) - filters.categories.forEach(c => params.append("category", c)) - - const response = await fetch(`/api/projects?${params.toString()}`) - - if (!response.ok) { - throw new Error("Failed to fetch projects") - } - - const data = await response.json() - setProjects(data.projects || []) - setFilterOptions(data.filterOptions || { fundingSources: [], cohorts: [], categories: [] }) - } catch (error) { - console.error("Failed to fetch projects:", error) - setError("Unable to load projects. Please try again later.") - } finally { - setIsLoading(false) - } - } - - fetchProjects() - }, [searchParams]) + const { projects, filterOptions, isLoading, error } = useProjects(filters) // Update URL when filters change const updateFilters = (updates: { @@ -154,7 +115,7 @@ export default function ProjectDirectoryPage() {

Error

-

{error}

+

{error.message}

) -} \ No newline at end of file +} + +export default memo(FilterPanel) \ No newline at end of file diff --git a/app/project-directory/components/ProjectDirectoryLayout.tsx b/app/project-directory/components/ProjectDirectoryLayout.tsx index 632be7b..75fe75b 100644 --- a/app/project-directory/components/ProjectDirectoryLayout.tsx +++ b/app/project-directory/components/ProjectDirectoryLayout.tsx @@ -1,4 +1,4 @@ -import { useState } from "react" +import { useState, useMemo } from "react" import FilterPanel from "./FilterPanel" import ProjectList from "./ProjectList" import { Button } from "@/components/ui/button" @@ -36,14 +36,17 @@ interface ProjectDirectoryLayoutProps { onToggleCategory, onProjectClick, isLoading = false, - }: ProjectDirectoryLayoutProps) { + }: ProjectDirectoryLayoutProps) { const [isMobileFilterOpen, setIsMobileFilterOpen] = useState(false) - const hasActiveFilters = - filters.searchQuery !== "" || - filters.fundingSources.length > 0 || - filters.cohorts.length > 0 || - filters.categories.length > 0 + const hasActiveFilters = useMemo( + () => + filters.searchQuery !== "" || + filters.fundingSources.length > 0 || + filters.cohorts.length > 0 || + filters.categories.length > 0, + [filters.searchQuery, filters.fundingSources.length, filters.cohorts.length, filters.categories.length] + ) return ( <> diff --git a/app/project-directory/components/ProjectList.tsx b/app/project-directory/components/ProjectList.tsx index bdb3160..1ca10e7 100644 --- a/app/project-directory/components/ProjectList.tsx +++ b/app/project-directory/components/ProjectList.tsx @@ -1,3 +1,4 @@ +import { memo } from "react" import ProjectCard from "./ProjectCard" import type { Project } from "@/types/project" @@ -6,7 +7,7 @@ interface ProjectListProps { onProjectClick: (_project: Project) => void } -export default function ProjectList({ projects, onProjectClick }: ProjectListProps) { +function ProjectList({ projects, onProjectClick }: ProjectListProps) { if (projects.length === 0) { return (
@@ -32,3 +33,5 @@ export default function ProjectList({ projects, onProjectClick }: ProjectListPro
) } + +export default memo(ProjectList) diff --git a/app/project-directory/page.tsx b/app/project-directory/page.tsx index 2967103..9f8672c 100644 --- a/app/project-directory/page.tsx +++ b/app/project-directory/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react" +import { useEffect, useState, useMemo, useCallback } from "react" import { useSearchParams, useRouter } from "next/navigation" import Header from "@/components/header" import Footer from "@/components/footer" @@ -15,33 +15,53 @@ export default function ProjectDirectoryPage() { const [selectedProject, setSelectedProject] = useState(null) const [isModalOpen, setIsModalOpen] = useState(false) - // Get current filters from URL - const filters = { - searchQuery: searchParams?.get("search") || "", - fundingSources: searchParams?.getAll("funding") || [], - cohorts: searchParams?.getAll("cohort") || [], - categories: searchParams?.getAll("category") || [], - } + const urlSearchQuery = searchParams?.get("search") || "" + const [localSearchQuery, setLocalSearchQuery] = useState(urlSearchQuery) + + useEffect(() => { + setLocalSearchQuery(urlSearchQuery) + }, [urlSearchQuery]) + + const debouncedUpdateURL = useMemo(() => { + let timeoutId: ReturnType + return (value: string) => { + clearTimeout(timeoutId) + timeoutId = setTimeout(() => { + const params = new URLSearchParams(searchParams?.toString() || "") + if (value.trim()) { + params.set("search", value.trim()) + } else { + params.delete("search") + } + router.replace(`?${params.toString()}`, { scroll: false }) + }, 300) + } + }, [searchParams, router]) + + const setSearchQuery = useCallback((query: string) => { + setLocalSearchQuery(query) + debouncedUpdateURL(query) + }, [debouncedUpdateURL]) + + const filters = useMemo( + () => ({ + searchQuery: urlSearchQuery, + fundingSources: searchParams?.getAll("funding") || [], + cohorts: searchParams?.getAll("cohort") || [], + categories: searchParams?.getAll("category") || [], + }), + [urlSearchQuery, searchParams] + ) const { projects, filterOptions, isLoading, error } = useProjects(filters) - // Update URL when filters change - const updateFilters = (updates: { - searchQuery?: string + const updateFilters = useCallback((updates: { fundingSources?: string[] cohorts?: string[] categories?: string[] }) => { const params = new URLSearchParams(searchParams?.toString() || "") - if (updates.searchQuery !== undefined) { - if (updates.searchQuery) { - params.set("search", updates.searchQuery) - } else { - params.delete("search") - } - } - if (updates.fundingSources !== undefined) { params.delete("funding") updates.fundingSources.forEach(f => params.append("funding", f)) @@ -56,47 +76,43 @@ export default function ProjectDirectoryPage() { params.delete("category") updates.categories.forEach(c => params.append("category", c)) } - - router.push(`?${params.toString()}`, { scroll: false }) - } - const setSearchQuery = (query: string) => { - updateFilters({ searchQuery: query }) - } + router.push(`?${params.toString()}`, { scroll: false }) + }, [searchParams, router]) - const toggleFundingSource = (source: string) => { + const toggleFundingSource = useCallback((source: string) => { const current = filters.fundingSources const updated = current.includes(source) ? current.filter((s) => s !== source) : [...current, source] updateFilters({ fundingSources: updated }) - } + }, [filters.fundingSources, updateFilters]) - const toggleCohort = (cohort: string) => { + const toggleCohort = useCallback((cohort: string) => { const current = filters.cohorts const updated = current.includes(cohort) ? current.filter((c) => c !== cohort) : [...current, cohort] updateFilters({ cohorts: updated }) - } + }, [filters.cohorts, updateFilters]) - const toggleCategory = (category: string) => { + const toggleCategory = useCallback((category: string) => { const current = filters.categories const updated = current.includes(category) ? current.filter((c) => c !== category) : [...current, category] updateFilters({ categories: updated }) - } + }, [filters.categories, updateFilters]) - const openProjectModal = (project: Project) => { + const openProjectModal = useCallback((project: Project) => { setSelectedProject(project) setIsModalOpen(true) - } + }, []) - const closeProjectModal = () => { + const closeProjectModal = useCallback(() => { setIsModalOpen(false) setTimeout(() => setSelectedProject(null), 200) - } + }, []) return (
@@ -128,7 +144,10 @@ export default function ProjectDirectoryPage() { /* Main Layout */ Date: Mon, 9 Feb 2026 14:16:08 -0500 Subject: [PATCH 18/53] chore: delete unused page --- app/project-directory/[id]/page.tsx | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 app/project-directory/[id]/page.tsx diff --git a/app/project-directory/[id]/page.tsx b/app/project-directory/[id]/page.tsx deleted file mode 100644 index 7b5d91f..0000000 --- a/app/project-directory/[id]/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { notFound } from "next/navigation" - -export default function ProjectDetailPage() { - notFound() -} From b00018ee3108cec738ce5ed783abffefb88be53b Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 14:18:58 -0500 Subject: [PATCH 19/53] feat: rename project-directory to projects --- .../components/FilterPanel.tsx | 0 .../components/ProjectCard.tsx | 0 .../components/ProjectDirectoryLayout.tsx | 0 .../components/ProjectList.tsx | 0 app/{project-directory => projects}/loading.tsx | 0 app/{project-directory => projects}/page.tsx | 4 ++-- 6 files changed, 2 insertions(+), 2 deletions(-) rename app/{project-directory => projects}/components/FilterPanel.tsx (100%) rename app/{project-directory => projects}/components/ProjectCard.tsx (100%) rename app/{project-directory => projects}/components/ProjectDirectoryLayout.tsx (100%) rename app/{project-directory => projects}/components/ProjectList.tsx (100%) rename app/{project-directory => projects}/loading.tsx (100%) rename app/{project-directory => projects}/page.tsx (99%) diff --git a/app/project-directory/components/FilterPanel.tsx b/app/projects/components/FilterPanel.tsx similarity index 100% rename from app/project-directory/components/FilterPanel.tsx rename to app/projects/components/FilterPanel.tsx diff --git a/app/project-directory/components/ProjectCard.tsx b/app/projects/components/ProjectCard.tsx similarity index 100% rename from app/project-directory/components/ProjectCard.tsx rename to app/projects/components/ProjectCard.tsx diff --git a/app/project-directory/components/ProjectDirectoryLayout.tsx b/app/projects/components/ProjectDirectoryLayout.tsx similarity index 100% rename from app/project-directory/components/ProjectDirectoryLayout.tsx rename to app/projects/components/ProjectDirectoryLayout.tsx diff --git a/app/project-directory/components/ProjectList.tsx b/app/projects/components/ProjectList.tsx similarity index 100% rename from app/project-directory/components/ProjectList.tsx rename to app/projects/components/ProjectList.tsx diff --git a/app/project-directory/loading.tsx b/app/projects/loading.tsx similarity index 100% rename from app/project-directory/loading.tsx rename to app/projects/loading.tsx diff --git a/app/project-directory/page.tsx b/app/projects/page.tsx similarity index 99% rename from app/project-directory/page.tsx rename to app/projects/page.tsx index 9f8672c..73a0e54 100644 --- a/app/project-directory/page.tsx +++ b/app/projects/page.tsx @@ -121,7 +121,7 @@ export default function ProjectDirectoryPage() {
{/* Header */}
-

Project Directory

+

Projects

A curated showcase of innovative startups and products built by founders and teams from V1 ecosystem.

@@ -140,7 +140,7 @@ export default function ProjectDirectoryPage() {
- ) : ( + ) : ( /* Main Layout */ Date: Mon, 9 Feb 2026 14:27:52 -0500 Subject: [PATCH 20/53] chore: remove tags for funding --- lib/notion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/notion.ts b/lib/notion.ts index 2ba2bd9..d910c36 100644 --- a/lib/notion.ts +++ b/lib/notion.ts @@ -136,7 +136,7 @@ export function transformNotionPageToProject(page: any): Project { website: null, })) : undefined, sectionType: hasInvestors ? "funding" : "cohort", - sectionName: hasInvestors ? properties.investors.multi_select[0].name : properties.cohort?.select?.name || "", + sectionName: hasInvestors ? parseInvestorName(properties.investors.multi_select[0].name).name : properties.cohort?.select?.name || "", sectionOrder: getCohortOrder(properties.cohort?.select?.name || ""), } } From c76fe07a67d96d3803c28a1759a87ba7ee34bfdc Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 16:12:18 -0500 Subject: [PATCH 21/53] feat: better cohort badges --- app/projects/components/ProjectCard.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/projects/components/ProjectCard.tsx b/app/projects/components/ProjectCard.tsx index 9ce5f02..a6c16eb 100644 --- a/app/projects/components/ProjectCard.tsx +++ b/app/projects/components/ProjectCard.tsx @@ -14,6 +14,15 @@ const vcFundColors = { "Sequoia Capital": "bg-red-500 text-white", } +// Cohort badge colors +const cohortColors = { + "Winter 2026 Product Studio Cohort": "bg-emerald-500 text-white", + "Fall 2025 Product Studio Cohort": "bg-purple-500 text-white", + "Winter 2025 Product Studio Cohort": "bg-cyan-500 text-white", + "Fall 2024 Product Studio Cohort": "bg-amber-600 text-white", + "Spring 2025 Product Studio Cohort": "bg-pink-500 text-white", +} + // Get cohort display name (remove "Product Studio Cohort" suffix) const getCohortDisplayName = (cohortName: string) => { return cohortName.replace(" Product Studio Cohort", "") @@ -24,6 +33,7 @@ export default function ProjectCard({ project, onClick }: ProjectCardProps) { const hasCohort = project.sectionType === "cohort" const vcBadgeColor = vcFundColors[project.sectionName as keyof typeof vcFundColors] || "bg-gray-500 text-white" + const cohortBadgeColor = cohortColors[project.sectionName as keyof typeof cohortColors] || "bg-gray-500 text-white" return (
)} {hasCohort && ( - + {getCohortDisplayName(project.sectionName)} )} From b00613e144eec7e08f95973d727a4f4d09be305e Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 16:59:08 -0500 Subject: [PATCH 22/53] refactor: spinner on data load when a filter is applied --- app/projects/components/FilterPanel.tsx | 12 ++- .../components/ProjectDirectoryLayout.tsx | 18 +++-- app/projects/page.tsx | 80 ++++++++++++++----- hooks/useProjects.ts | 1 - 4 files changed, 77 insertions(+), 34 deletions(-) diff --git a/app/projects/components/FilterPanel.tsx b/app/projects/components/FilterPanel.tsx index 8c9629f..6d9247c 100644 --- a/app/projects/components/FilterPanel.tsx +++ b/app/projects/components/FilterPanel.tsx @@ -18,6 +18,7 @@ interface FilterPanelProps { onToggleFunding: (_source: string) => void onToggleCohort: (_cohort: string) => void onToggleCategory: (_category: string) => void + isLoading?: boolean } function FilterPanel({ @@ -27,6 +28,7 @@ function FilterPanel({ onToggleFunding, onToggleCohort, onToggleCategory, + isLoading = false, }: FilterPanelProps) { return (
@@ -45,6 +47,7 @@ function FilterPanel({ value={filters.searchQuery} onChange={(e) => onSearchChange(e.target.value)} className="pl-10" + disabled={isLoading} />
@@ -59,13 +62,14 @@ function FilterPanel({ {filterOptions.fundingSources.map((source) => ( @@ -85,13 +89,14 @@ function FilterPanel({ return ( @@ -110,13 +115,14 @@ function FilterPanel({ {filterOptions.categories.map((category) => ( diff --git a/app/projects/components/ProjectDirectoryLayout.tsx b/app/projects/components/ProjectDirectoryLayout.tsx index 75fe75b..c9f1baa 100644 --- a/app/projects/components/ProjectDirectoryLayout.tsx +++ b/app/projects/components/ProjectDirectoryLayout.tsx @@ -62,6 +62,7 @@ interface ProjectDirectoryLayoutProps { onToggleFunding={onToggleFunding} onToggleCohort={onToggleCohort} onToggleCategory={onToggleCategory} + isLoading={isLoading} />
@@ -154,14 +155,15 @@ interface ProjectDirectoryLayoutProps { {/* Filter Content */}
- +
{/* Footer */} diff --git a/app/projects/page.tsx b/app/projects/page.tsx index 73a0e54..250bc2d 100644 --- a/app/projects/page.tsx +++ b/app/projects/page.tsx @@ -22,6 +22,37 @@ export default function ProjectDirectoryPage() { setLocalSearchQuery(urlSearchQuery) }, [urlSearchQuery]) + const [localFilters, setLocalFilters] = useState({ + searchQuery: urlSearchQuery, + fundingSources: searchParams?.getAll("funding") || [], + cohorts: searchParams?.getAll("cohort") || [], + categories: searchParams?.getAll("category") || [], + }) + + const [isRefetching, setIsRefetching] = useState(false) + const [cachedFilterOptions, setCachedFilterOptions] = useState({ + fundingSources: [] as string[], + cohorts: [] as string[], + categories: [] as string[], + }) + + useEffect(() => { + setLocalFilters({ + searchQuery: urlSearchQuery, + fundingSources: searchParams?.getAll("funding") || [], + cohorts: searchParams?.getAll("cohort") || [], + categories: searchParams?.getAll("category") || [], + }) + }, [urlSearchQuery, searchParams]) + + const { projects, filterOptions, isLoading, error } = useProjects(localFilters) + + useEffect(() => { + if (filterOptions.fundingSources.length > 0 || filterOptions.cohorts.length > 0 || filterOptions.categories.length > 0) { + setCachedFilterOptions(filterOptions) + } + }, [filterOptions]) + const debouncedUpdateURL = useMemo(() => { let timeoutId: ReturnType return (value: string) => { @@ -40,20 +71,19 @@ export default function ProjectDirectoryPage() { const setSearchQuery = useCallback((query: string) => { setLocalSearchQuery(query) + setLocalFilters(prev => ({ ...prev, searchQuery: query })) debouncedUpdateURL(query) }, [debouncedUpdateURL]) - const filters = useMemo( - () => ({ - searchQuery: urlSearchQuery, - fundingSources: searchParams?.getAll("funding") || [], - cohorts: searchParams?.getAll("cohort") || [], - categories: searchParams?.getAll("category") || [], - }), - [urlSearchQuery, searchParams] - ) + useEffect(() => { + if (isLoading && !isRefetching) { + setIsRefetching(true) + } else if (!isLoading && isRefetching) { + setIsRefetching(false) + } + }, [isLoading, isRefetching]) - const { projects, filterOptions, isLoading, error } = useProjects(filters) + const isFilterLoading = isRefetching || isLoading const updateFilters = useCallback((updates: { fundingSources?: string[] @@ -81,28 +111,34 @@ export default function ProjectDirectoryPage() { }, [searchParams, router]) const toggleFundingSource = useCallback((source: string) => { - const current = filters.fundingSources + setIsRefetching(true) + const current = localFilters.fundingSources const updated = current.includes(source) - ? current.filter((s) => s !== source) + ? current.filter((s: string) => s !== source) : [...current, source] + setLocalFilters(prev => ({ ...prev, fundingSources: updated })) updateFilters({ fundingSources: updated }) - }, [filters.fundingSources, updateFilters]) + }, [localFilters.fundingSources, updateFilters]) const toggleCohort = useCallback((cohort: string) => { - const current = filters.cohorts + setIsRefetching(true) + const current = localFilters.cohorts const updated = current.includes(cohort) - ? current.filter((c) => c !== cohort) + ? current.filter((c: string) => c !== cohort) : [...current, cohort] + setLocalFilters(prev => ({ ...prev, cohorts: updated })) updateFilters({ cohorts: updated }) - }, [filters.cohorts, updateFilters]) + }, [localFilters.cohorts, updateFilters]) const toggleCategory = useCallback((category: string) => { - const current = filters.categories + setIsRefetching(true) + const current = localFilters.categories const updated = current.includes(category) - ? current.filter((c) => c !== category) + ? current.filter((c: string) => c !== category) : [...current, category] + setLocalFilters(prev => ({ ...prev, categories: updated })) updateFilters({ categories: updated }) - }, [filters.categories, updateFilters]) + }, [localFilters.categories, updateFilters]) const openProjectModal = useCallback((project: Project) => { setSelectedProject(project) @@ -145,16 +181,16 @@ export default function ProjectDirectoryPage() { )} diff --git a/hooks/useProjects.ts b/hooks/useProjects.ts index 55d96f1..0177e28 100644 --- a/hooks/useProjects.ts +++ b/hooks/useProjects.ts @@ -45,7 +45,6 @@ export function useProjects(params: ProjectsQueryParams) { } = useQuery({ queryKey: ["projects", params], queryFn: () => fetchProjects(params), - placeholderData: (previousData) => previousData, staleTime: 5 * 60 * 1000, retry: 3, }); From ee8e172aae8c14329be29fa4e78d571936918fe5 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 17:11:08 -0500 Subject: [PATCH 23/53] feat: provide proper founder connects and hide company website if n/a --- components/founder-card.tsx | 16 ++++++++++++---- components/project-modal.tsx | 20 +++++++++++--------- lib/notion.ts | 5 ++++- types/project.ts | 1 + 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/components/founder-card.tsx b/components/founder-card.tsx index 8a1703c..5678260 100644 --- a/components/founder-card.tsx +++ b/components/founder-card.tsx @@ -5,9 +5,10 @@ interface FounderCardProps { name: string role: string imageSrc: string + contactUrl?: string } -export default function FounderCard({ name, role, imageSrc }: FounderCardProps) { +export default function FounderCard({ name, role, imageSrc, contactUrl }: FounderCardProps) { return (
@@ -16,9 +17,16 @@ export default function FounderCard({ name, role, imageSrc }: FounderCardProps)

{name}

{role}

- + {contactUrl && ( + + Connect + + )}
) diff --git a/components/project-modal.tsx b/components/project-modal.tsx index 617fab0..538e3b6 100644 --- a/components/project-modal.tsx +++ b/components/project-modal.tsx @@ -66,14 +66,16 @@ export default function ProjectModal({ project, isOpen, onClose }: ProjectModalP

{project.companyName}

- - Company website - + {project.companyWebsite && ( + + Company website + + )}
{project.categories.map((category, index) => ( @@ -93,7 +95,7 @@ export default function ProjectModal({ project, isOpen, onClose }: ProjectModalP

Founders

{project.founders.map((founder) => ( - + ))}
diff --git a/lib/notion.ts b/lib/notion.ts index d910c36..22afc75 100644 --- a/lib/notion.ts +++ b/lib/notion.ts @@ -113,6 +113,8 @@ export function transformNotionPageToProject(page: any): Project { const { id, properties } = page const nameInfo = extractTitleInfo(properties.name) const hasInvestors = properties.investors?.multi_select?.length > 0 + const foundersList = properties.founders?.multi_select || [] + const contactsList = properties.contacts?.multi_select || [] return { id, @@ -123,11 +125,12 @@ export function transformNotionPageToProject(page: any): Project { companyName: nameInfo.name, companyWebsite: properties.website?.url || nameInfo.link || null, categories: extractMultiSelect(properties.categories), - founders: properties.founders?.multi_select?.map((f: any) => ({ + founders: foundersList.map((f: any, index: number) => ({ id: f.id, name: f.name, role: "Founder", imageSrc: generatePlaceholderImage(f.name, "24x24"), + contactUrl: contactsList[index]?.name || null, })) || [], investors: hasInvestors ? properties.investors.multi_select?.map((inv: any) => ({ id: inv.id, diff --git a/types/project.ts b/types/project.ts index 7554035..6289953 100644 --- a/types/project.ts +++ b/types/project.ts @@ -7,6 +7,7 @@ export interface Founder { name: string role: "Founder" imageSrc: string + contactUrl?: string } export interface Investor { From e574869c2c660493afd2caf07f68ca4c10eb1f08 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 17:12:17 -0500 Subject: [PATCH 24/53] refactor: hide founders section if none are present --- components/project-modal.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/components/project-modal.tsx b/components/project-modal.tsx index 538e3b6..ba4a33d 100644 --- a/components/project-modal.tsx +++ b/components/project-modal.tsx @@ -91,14 +91,16 @@ export default function ProjectModal({ project, isOpen, onClose }: ProjectModalP

{project.overview}

-
-

Founders

-
- {project.founders.map((founder) => ( - - ))} -
-
+ {project.founders.length > 0 && ( +
+

Founders

+
+ {project.founders.map((founder) => ( + + ))} +
+
+ )}
) From 18e069e4fc6e3355611dcfd18a70910d4fa075d1 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 17:38:01 -0500 Subject: [PATCH 25/53] feat: weighted ordering of funded projects --- lib/notion.ts | 118 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/lib/notion.ts b/lib/notion.ts index 22afc75..c8b73f5 100644 --- a/lib/notion.ts +++ b/lib/notion.ts @@ -1,4 +1,82 @@ -import type { Project } from "@/types/project" +import type { Project, Investor } from "@/types/project" + +export type PrestigeTier = "top" | "high" | "standard" + +const INVESTOR_TIERS: Record = { + "Sequoia Capital": "top", + "General Catalyst": "top", + "Y Combinator": "top", + "Techstars": "top", + "SignalFire": "high", + "Contrary Capital": "high", + "Pioneer Fund": "high", + "Agent Fund": "high", + "Moxxie Ventures": "high", + "Streamlined Ventures": "high", + "Lobster Capital": "high", + "Gold House Ventures": "high", + "GC Venture Fellows": "high", + "Z Fellows": "high", + "Bling": "standard", + "Heliconia Capital": "standard", + "Armajaro Holdings": "standard", + "Transpose Platform Management": "standard", + "UMich Center for Entrepreneurship": "standard", + "Zell Lurie Institute": "standard", + "Paul Graham": "high", + "Stephen Wolfram": "high", + "Paul Copplestone": "high", + "Karim Atiyeh": "high", +} + +const TIER_SCORES: Record = { + top: 100, + high: 50, + standard: 10, +} + +export function getInvestorTier(investorName: string): PrestigeTier { + const baseName = investorName.includes("Techstars") ? "Techstars" : investorName + return INVESTOR_TIERS[baseName] || "standard" +} + +export function getInvestorPrestigeScore(investorName: string, investorType: string): number { + const tier = getInvestorTier(investorName) + const score = TIER_SCORES[tier] + + if (investorType === "angel" && tier === "standard") { + return score + 5 + } + + return score +} + +export function sortInvestorsByPrestige(investors: Investor[]): Investor[] { + return [...investors].sort((a, b) => { + const scoreA = getInvestorPrestigeScore(a.name, a.type) + const scoreB = getInvestorPrestigeScore(b.name, b.type) + + if (scoreA !== scoreB) { + return scoreB - scoreA + } + + return a.name.localeCompare(b.name) + }) +} + +export function getProjectPrestigeScore(project: Project): number { + if (!project.investors || project.investors.length === 0) { + return 0 + } + + const maxScore = Math.max( + ...project.investors.map(inv => getInvestorPrestigeScore(inv.name, inv.type)) + ) + + const countBonus = Math.min(project.investors.length - 1, 3) * 5 + + return maxScore + countBonus +} export function extractTitleInfo(titleProp: any): { name: string; link: string | null } { const titleText = titleProp.title?.[0]?.text @@ -51,6 +129,18 @@ export function sortProjects(projects: Project[]): Project[] { if (a.sectionType !== b.sectionType) { return a.sectionType === "funding" ? -1 : 1 } + + if (a.sectionType === "funding" && b.sectionType === "funding") { + const prestigeA = getProjectPrestigeScore(a) + const prestigeB = getProjectPrestigeScore(b) + + if (prestigeA !== prestigeB) { + return prestigeB - prestigeA + } + + return a.companyName.localeCompare(b.companyName) + } + return b.sectionOrder - a.sectionOrder }) } @@ -62,7 +152,11 @@ export function extractFilterOptions(projects: Project[]) { projects.forEach(project => { if (project.sectionType === "funding" && project.sectionName) { - fundingSources.add(project.sectionName) + const topInvestor = project.investors?.[0] + if (topInvestor && + (topInvestor.type === "vc" || topInvestor.type === "accelerator")) { + fundingSources.add(project.sectionName) + } } else if (project.sectionType === "cohort") { cohorts.add(project.sectionName) } @@ -116,6 +210,15 @@ export function transformNotionPageToProject(page: any): Project { const foundersList = properties.founders?.multi_select || [] const contactsList = properties.contacts?.multi_select || [] + const rawInvestors = hasInvestors ? properties.investors.multi_select?.map((inv: any) => ({ + id: inv.id, + name: parseInvestorName(inv.name).name, + type: parseInvestorName(inv.name).type, + website: null, + })) : undefined + + const sortedInvestors = rawInvestors ? sortInvestorsByPrestige(rawInvestors) : undefined + return { id, title: nameInfo.name, @@ -132,14 +235,11 @@ export function transformNotionPageToProject(page: any): Project { imageSrc: generatePlaceholderImage(f.name, "24x24"), contactUrl: contactsList[index]?.name || null, })) || [], - investors: hasInvestors ? properties.investors.multi_select?.map((inv: any) => ({ - id: inv.id, - name: parseInvestorName(inv.name).name, - type: parseInvestorName(inv.name).type, - website: null, - })) : undefined, + investors: sortedInvestors, sectionType: hasInvestors ? "funding" : "cohort", - sectionName: hasInvestors ? parseInvestorName(properties.investors.multi_select[0].name).name : properties.cohort?.select?.name || "", + sectionName: hasInvestors + ? sortedInvestors?.[0]?.name || "" + : properties.cohort?.select?.name || "", sectionOrder: getCohortOrder(properties.cohort?.select?.name || ""), } } From b647b6895b84c1988a78ee26b540c4c601e243f1 Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 19:06:29 -0500 Subject: [PATCH 26/53] feat: better search bar --- app/projects/components/FilterPanel.tsx | 22 +---- .../components/ProjectDirectoryLayout.tsx | 99 +++++++++++-------- 2 files changed, 60 insertions(+), 61 deletions(-) diff --git a/app/projects/components/FilterPanel.tsx b/app/projects/components/FilterPanel.tsx index 6d9247c..b18d26d 100644 --- a/app/projects/components/FilterPanel.tsx +++ b/app/projects/components/FilterPanel.tsx @@ -1,5 +1,4 @@ import { memo } from "react" -import { Input } from "@/components/ui/input" import { Search, Building, Users, Tag } from "lucide-react" interface FilterPanelProps { @@ -19,6 +18,7 @@ interface FilterPanelProps { onToggleCohort: (_cohort: string) => void onToggleCategory: (_category: string) => void isLoading?: boolean + showSearch?: boolean } function FilterPanel({ @@ -29,29 +29,15 @@ function FilterPanel({ onToggleCohort, onToggleCategory, isLoading = false, + showSearch = true, }: FilterPanelProps) { return (
- {/* Header */} + {/* Header */}

Filters

- {/* Search */} -
-
- - onSearchChange(e.target.value)} - className="pl-10" - disabled={isLoading} - /> -
-
- {/* Funding Sources */}
@@ -133,4 +119,4 @@ function FilterPanel({ ) } -export default memo(FilterPanel) \ No newline at end of file +export default memo(FilterPanel) diff --git a/app/projects/components/ProjectDirectoryLayout.tsx b/app/projects/components/ProjectDirectoryLayout.tsx index c9f1baa..a844872 100644 --- a/app/projects/components/ProjectDirectoryLayout.tsx +++ b/app/projects/components/ProjectDirectoryLayout.tsx @@ -26,17 +26,17 @@ interface ProjectDirectoryLayoutProps { isLoading?: boolean } - export default function ProjectDirectoryLayout({ - projects, - filters, - filterOptions, - onSearchChange, - onToggleFunding, - onToggleCohort, - onToggleCategory, - onProjectClick, - isLoading = false, - }: ProjectDirectoryLayoutProps) { +export default function ProjectDirectoryLayout({ + projects, + filters, + filterOptions, + onSearchChange, + onToggleFunding, + onToggleCohort, + onToggleCategory, + onProjectClick, + isLoading = false, +}: ProjectDirectoryLayoutProps) { const [isMobileFilterOpen, setIsMobileFilterOpen] = useState(false) const hasActiveFilters = useMemo( @@ -54,7 +54,7 @@ interface ProjectDirectoryLayoutProps {
{/* Filter Panel - Left Side */}
-
+
- {/* Project List - Right Side */} + {/* Project List - Right Side */}
+
+
+ onSearchChange(e.target.value)} + className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" + /> +
+
+

Projects ({projects.length})

-
- {isLoading ? ( -
-
-
- ) : ( - - )} +
+ {isLoading ? ( +
+
+
+ ) : ( + + )}
{/* Mobile/Tablet Layout */}
- {/* Mobile Filter Button */}
-
+ {/* Mobile Filter Button */} {/* Project Count */}

Projects ({projects.length}) -

+
{/* Loading state */} @@ -153,18 +165,19 @@ interface ProjectDirectoryLayoutProps {
- {/* Filter Content */} -
- -
+ {/* Filter Content */} +
+ +
{/* Footer */}
@@ -181,4 +194,4 @@ interface ProjectDirectoryLayoutProps {
) -} \ No newline at end of file +} From 0ccee82ff266d5ebbefce7f47a880fee4ba6e9ac Mon Sep 17 00:00:00 2001 From: Kieran Llarena Date: Mon, 9 Feb 2026 19:25:39 -0500 Subject: [PATCH 27/53] feat: improved left side filtering ui --- app/projects/components/FilterPanel.tsx | 21 +++---------------- app/projects/components/ProjectCard.tsx | 4 ++-- .../components/ProjectDirectoryLayout.tsx | 6 +++--- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/app/projects/components/FilterPanel.tsx b/app/projects/components/FilterPanel.tsx index b18d26d..17ec03d 100644 --- a/app/projects/components/FilterPanel.tsx +++ b/app/projects/components/FilterPanel.tsx @@ -1,5 +1,4 @@ import { memo } from "react" -import { Search, Building, Users, Tag } from "lucide-react" interface FilterPanelProps { filters: { @@ -33,17 +32,9 @@ function FilterPanel({ }: FilterPanelProps) { return (
- {/* Header */} -
-

Filters

-
- {/* Funding Sources */}
-
- -

Funding

-
+

Funding

{filterOptions.fundingSources.map((source) => (