From 787e91c1627f09ef6e6421cc2c02a50c9695f022 Mon Sep 17 00:00:00 2001 From: Tony Kan Date: Fri, 29 Nov 2024 01:25:53 -0800 Subject: [PATCH] feat(projects): Implement Projects page and separate data - Implemented Projects page with dynamic project listing. - Separated project data into a dedicated file. - Added automatic and manual scrolling for highlighted projects. --- data/events.mjs | 4 +- data/projects.mjs | 17 +++++ pages/projects.jsx | 173 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 data/projects.mjs create mode 100644 pages/projects.jsx diff --git a/data/events.mjs b/data/events.mjs index 60fc332..b1c5426 100644 --- a/data/events.mjs +++ b/data/events.mjs @@ -13,6 +13,6 @@ * @property {string} link The link to the event page. Must be in the /events directory. * @property {boolean} archived Whether the event is archived or not. Archived events are not displayed on the events page. */ -const eventsData = []; +const events = []; -export default eventsData; +export default events; diff --git a/data/projects.mjs b/data/projects.mjs new file mode 100644 index 0000000..47decd5 --- /dev/null +++ b/data/projects.mjs @@ -0,0 +1,17 @@ +/** + * @fileoverview Projects Data + * @description Data for the projects page. + */ + +/** + * @type {Project[]} + * @typedef {Object} Project + * @property {string} name The name of the project. + * @property {string} description The description of the project. + * @property {string} backgroundImage The path to the background image of the project. Must be in the public/images/projects directory. + * @property {string} link The link to the project page. Must be a valid URL. (a demo, a GitHub repo, YouTube video, etc.) + * @property {boolean} isHighlighted Whether the project is highlighted or not. Highlighted projects are displayed in the top scrolling section. + */ +const projects = []; + +export default projects; diff --git a/pages/projects.jsx b/pages/projects.jsx new file mode 100644 index 0000000..5883652 --- /dev/null +++ b/pages/projects.jsx @@ -0,0 +1,173 @@ +/** + * @fileoverview Projects Page + * @description The projects page of the website. (NOTE: Data is imported from data/projects.mjs) + */ + +import { useEffect, useState, useRef } from 'react'; +import projectsData from '../data/projects.mjs'; + +export default function ProjectsPage() { + const [highlightedProjects, setHighlightedProjects] = useState([]); + const [otherProjects, setOtherProjects] = useState([]); + const sliderRef = useRef(null); + const slideIntervalRef = useRef(null); + const currentIndex = useRef(0); + + // Separate projects into highlighted and others + useEffect(() => { + const highlighted = projectsData.filter((project) => project.isHighlighted); + const others = projectsData.filter((project) => !project.isHighlighted); + setHighlightedProjects(highlighted); + setOtherProjects(others); + }, []); + + // Auto-scroll highlighted projects every 3 seconds + useEffect(() => { + if (highlightedProjects.length > 0) { + slideIntervalRef.current = setInterval(() => { + const nextIndex = + (currentIndex.current + 1) % highlightedProjects.length; + currentIndex.current = nextIndex; + sliderRef.current?.scrollTo({ + left: nextIndex * sliderRef.current.clientWidth, + behavior: 'smooth', + }); + }, 3000); + } + + return () => clearInterval(slideIntervalRef.current); + }, [highlightedProjects]); + + // Scroll to the previous highlighted project + const handlePrevClick = () => { + const prevIndex = + (currentIndex.current - 1 + highlightedProjects.length) % + highlightedProjects.length; + currentIndex.current = prevIndex; + sliderRef.current?.scrollTo({ + left: prevIndex * sliderRef.current.clientWidth, + behavior: 'smooth', + }); + }; + + // Scroll to the next highlighted project + const handleNextClick = () => { + const nextIndex = (currentIndex.current + 1) % highlightedProjects.length; + currentIndex.current = nextIndex; + sliderRef.current?.scrollTo({ + left: nextIndex * sliderRef.current.clientWidth, + behavior: 'smooth', + }); + }; + + return ( +
+ {/* Page Header */} +

Projects

+ + {/* Highlighted Projects Section */} +
+ {highlightedProjects.length > 0 ? ( +
+
+ {highlightedProjects.map((project) => ( +
+
+
+

{project.name}

+

{project.description}

+ {project.link && ( + + Learn More + + )} +
+
+ ))} +
+ {/* Scroll Controls */} + + +
+ ) : ( +
+

No Highlighted Projects

+

+ Explore all projects below or check back later! +

+
+ )} +
+ + {/* All Projects Section */} +
+ {[...highlightedProjects, ...otherProjects].length > 0 ? ( + [...highlightedProjects, ...otherProjects].map((project) => ( +
+
+ {!project.backgroundImage && ( +
+ )} +
+
+

{project.name}

+

{project.description}

+ {project.link && ( + + Learn More + + )} +
+
+ )) + ) : ( +
+

No Projects Available

+

Check back soon for updates!

+
+ )} +
+
+ ); +}