From b3b472ecb292f4a28dfd5a0c6b79de60d4c05809 Mon Sep 17 00:00:00 2001 From: Korbin101lee Date: Sat, 10 May 2025 14:37:55 -0700 Subject: [PATCH 1/2] Added Git activity to the website as well as the branch websites --- src/app/(main)/(routes)/projects/content.tsx | 1726 ++++++++++------- src/app/(main)/(routes)/projects/page.tsx | 320 +-- .../(routes)/projects/project-styles.css | 120 +- src/app/api/github/github-service.ts | 275 +++ src/app/api/github/route.ts | 45 + src/app/components/Projects/GitMonitor.tsx | 250 ++- 6 files changed, 1763 insertions(+), 973 deletions(-) create mode 100644 src/app/api/github/github-service.ts create mode 100644 src/app/api/github/route.ts diff --git a/src/app/(main)/(routes)/projects/content.tsx b/src/app/(main)/(routes)/projects/content.tsx index d0e04f2..9e2b897 100644 --- a/src/app/(main)/(routes)/projects/content.tsx +++ b/src/app/(main)/(routes)/projects/content.tsx @@ -1,774 +1,1012 @@ -import { Backpack, Gamepad2, Hammer, PanelsTopLeft, Smartphone, FileOutput } from 'lucide-react'; -import getSVG from '~/public/svg-data'; -import Link from 'next/link'; -import Image from 'next/image' -import ApplyButton from '~/src/app/components/Projects/ApplyButton'; -import { getMember } from '~/src/app/components/members'; -import { MemberID } from '~/src/app/components/members'; -import { ExternalLink } from 'lucide-react'; +import { + Backpack, + Gamepad2, + Hammer, + PanelsTopLeft, + Smartphone, + FileOutput, +} from "lucide-react"; +import getSVG from "~/public/svg-data"; +import Link from "next/link"; +import Image from "next/image"; +import ApplyButton from "~/src/app/components/Projects/ApplyButton"; +import { getMember } from "~/src/app/components/members"; +import { MemberID } from "~/src/app/components/members"; +import { ExternalLink } from "lucide-react"; +import GitMonitor from "~/src/app/components/Projects/GitMonitor"; export const projectIDs = [ - 'academy', - 'website', - 'gamedev', - 'mobile', - 'scraper', - + "academy", + "website", + "gamedev", + "mobile", + "scraper", ] as const; export type ProjectID = typeof projectIDs[number]; - export const projectApplicationLinks = { - academy: 'https://docs.google.com/forms/d/e/1FAIpQLSdO_e-4Sl1JtNxqP674A8ZFL57gcwEu5piLhicXDE44rw9KDQ/viewform?usp=sf_link', - website: 'https://docs.google.com/forms/d/e/1FAIpQLSd5DWM45tlvF0rzjzHmhI3nRrU5EzuztpMrHMrRwAiXdGl-jg/viewform?usp=sf_link', - gamedev: 'https://docs.google.com/forms/d/e/1FAIpQLSdkWDK_idZLQJ8X98-9iCYxnwTF03ji3QRdzwiU5wz8d3h8hg/viewform?usp=sf_link', - mobile: 'https://docs.google.com/forms/d/e/1FAIpQLSd--0dToZAGV4MThGzuiJuSy_GCmFIoPjkgRjAbBPnk0B6MrA/viewform?usp=sf_link', - scraper: 'https://docs.google.com/forms/d/e/1FAIpQLSfJHHugJUM3Sc_r_A7nCDjfSyaIXTepd90raUZ_zEwVZ_mEMg/viewform?usp=sf_link', - } + academy: + "https://docs.google.com/forms/d/e/1FAIpQLSdO_e-4Sl1JtNxqP674A8ZFL57gcwEu5piLhicXDE44rw9KDQ/viewform?usp=sf_link", + website: + "https://docs.google.com/forms/d/e/1FAIpQLSd5DWM45tlvF0rzjzHmhI3nRrU5EzuztpMrHMrRwAiXdGl-jg/viewform?usp=sf_link", + gamedev: + "https://docs.google.com/forms/d/e/1FAIpQLSdkWDK_idZLQJ8X98-9iCYxnwTF03ji3QRdzwiU5wz8d3h8hg/viewform?usp=sf_link", + mobile: + "https://docs.google.com/forms/d/e/1FAIpQLSd--0dToZAGV4MThGzuiJuSy_GCmFIoPjkgRjAbBPnk0B6MrA/viewform?usp=sf_link", + scraper: + "https://docs.google.com/forms/d/e/1FAIpQLSfJHHugJUM3Sc_r_A7nCDjfSyaIXTepd90raUZ_zEwVZ_mEMg/viewform?usp=sf_link", +}; export const gitRepoLinks = { - academy: "https://github.com/ASU-CodeDevils/CD-Academy", - website: "https://github.com/ASU-CodeDevils/codedevils.org", - gamedev: "https://github.com/ASU-CodeDevils/CD-GameDev", - mobile: "https://github.com/ASU-CodeDevils/CD-Mobile", - scraper: "https://github.com/ASU-CodeDevils/scraper.codedevils.org", - }; + academy: "https://github.com/ASU-CodeDevils/CD-Academy", + website: "https://github.com/ASU-CodeDevils/codedevils.org", + gamedev: "https://github.com/ASU-CodeDevils/CD-GameDev", + mobile: "https://github.com/ASU-CodeDevils/CD-Mobile", + scraper: "https://github.com/ASU-CodeDevils/scraper.codedevils.org", +}; +// format: element [0] must be of type MemberID all other elements must be an array of one or +// two string literals. First element is the contributor's name, second is an optional link. - // format: element [0] must be of type MemberID all other elements must be an array of one or - // two string literals. First element is the contributor's name, second is an optional link. - const projectTeams = { - academy: [['pita'], - ['Allie Betlewicz'], - ['John Miller'], - ['Jacob Kobrick'], - ['Adrian Parra'], - ], - website: [ ['pita'], - ['Juliana Bush'], - ['Ella Rushing'], - ['Boubacar S Diiallo'], - ['John Miller'], - ['Rhett Harrison'], - ['David Nevarez', 'https://davidnevarez.info/'], - ['Alejandro Jimenez'], - ['Darshan Phaldesai'], - ['Jaith Darrah'], - ['Tyler Wallace'] - ], - gamedev: [ ['remi'], - ['Autumn O'], - ['Pita Sherwood', 'https://pita.blue'], - ['John Miller'], - ['Alejandro Jimenez'], - ['Josh Walker'], - ['Carter Hollman'], - ['Jaith Darrah', 'https://www.jaithdarrah.com/'], - ['Krishnaprasad Sreekum'] - ], - mobile: [['pita'], - ['John Miller'], - ['Alejandro Jimenez'], - ['Korbin Hillan'], - ], - scraper: [ - ['frankie'], - ['Logan Wright'], - ['Carter Hollman'], - ['Derrick Berko'], - ['Michael Ellmer'] - ] -} - + academy: [ + ["pita"], + ["Allie Betlewicz"], + ["John Miller"], + ["Jacob Kobrick"], + ["Adrian Parra"], + ], + website: [ + ["pita"], + ["Juliana Bush"], + ["Ella Rushing"], + ["Boubacar S Diiallo"], + ["John Miller"], + ["Rhett Harrison"], + ["David Nevarez", "https://davidnevarez.info/"], + ["Alejandro Jimenez"], + ["Darshan Phaldesai"], + ["Jaith Darrah"], + ["Tyler Wallace"], + ], + gamedev: [ + ["remi"], + ["Autumn O"], + ["Pita Sherwood", "https://pita.blue"], + ["John Miller"], + ["Alejandro Jimenez"], + ["Josh Walker"], + ["Carter Hollman"], + ["Jaith Darrah", "https://www.jaithdarrah.com/"], + ["Krishnaprasad Sreekum"], + ], + mobile: [["pita"], ["John Miller"], ["Alejandro Jimenez"], ["Korbin Hillan"]], + scraper: [ + ["frankie"], + ["Logan Wright"], + ["Carter Hollman"], + ["Derrick Berko"], + ["Michael Ellmer"], + ], +}; - - const processContent = ({projectID, contentID, pageID}:{ - projectID: ProjectID, - contentID: number, - pageID: string - }) => { - - const content = { - academy: [( -
-

CodeDevils Academy

-
- -
-
- Join the Project! -
-
- ), ( -

A sister website to CodeDevils.org. A content library for users to visit and learn how to code!

- ), ( - <> -

- The sister website to CodeDevils.org, CodeDevils Academy is our content management and learning - platform. We're excited to announce the development of our dedicated learning platform designed - to empower our community through accessible educational resources. This project will be built - from the ground up, although the designs for it are in the late stages. Our vision for CodeDevils - Academy is to create an engaging, user-friendly environment where members can easily access a wealth - of learning materials tailored to their needs. -

- We aim to provide a seamless experience for all users. The platform will feature a clean and organized - layout, allowing members to quickly find exclusive tutorials, courses, and resources without hassle. - We plan to create a robust content management system that will allow us to upload, edit, and manage - educational materials efficiently. -

- The platform will host a variety of content formats, including written tutorials, video lessons, - quizzes, and coding challenges. By offering multiple learning styles, we aim to engage all members, - whether they are beginners or advanced developers. To keep our members motivated, we'll implement - progress tracking features that allow users to monitor their advancement through courses and tutorials. - This will help them set goals and celebrate their achievements. -

- If you're ready to make a difference and help us build CodeDevils Academy, we invite you to apply. - Let's create a premier learning platform that reflects our commitment to education and community. - Your journey with CodeDevils starts here—let's make something great! -

- - ), ( -
-
-
-

Current Project Goals

-
-
-
    -
  • Implement a content player component
  • -
  • Create user authentication systems
  • -
  • Finalize front end and admin panel
  • -
- -
-
- ), ( -
- - { getSVG({ svgID: 3 }) } - - - { getSVG({ svgID: 2 }) } - - - { getSVG({ svgID: 4 }) } - -
- ), ( -
-
GitHub Activity
-
-
- ), ( -
- project image - project image - project image -
- ), ( -
-
-
-

Project Team

-
-
- Project Leader's photo -
-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'name'}) }

-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'bio'}) }

-
-
-
- { projectTeams[projectID][1] ? - ( projectTeams[projectID].map(([member, link], index) => ( - index > 0 ? ( -
- { link ? {member} - :

{member}

} -
) : <> - ))) : <>

No contributors just yet. Be the first to apply!

} -
-
- ), - ( - - ) - ], - - website: [( -
-

CodeDevils Website

-
- -
-
- Join the Project! -
-
- ), ( -

Our rapidly-evolving website. New features and tools are constantly being added!

- ), ( +const processContent = ({ + projectID, + contentID, + pageID, +}: { + projectID: ProjectID; + contentID: number; + pageID: string; +}) => { + const content = { + academy: [ +
+

CodeDevils Academy

+
+ +
+
+ Join the Project! +
+
, +

+ A sister website to CodeDevils.org. A content library for users to visit + and learn how to code! +

, + <> +

+ The sister website to CodeDevils.org, CodeDevils Academy is our + content management and learning platform. We're excited to + announce the development of our dedicated learning platform designed + to empower our community through accessible educational resources. + This project will be built from the ground up, although the designs + for it are in the late stages. Our vision for CodeDevils Academy is to + create an engaging, user-friendly environment where members can easily + access a wealth of learning materials tailored to their needs. +

+

+ We aim to provide a seamless experience for all users. The platform + will feature a clean and organized layout, allowing members to quickly + find exclusive tutorials, courses, and resources without hassle. We + plan to create a robust content management system that will allow us + to upload, edit, and manage educational materials efficiently. +

+

+ The platform will host a variety of content formats, including written + tutorials, video lessons, quizzes, and coding challenges. By offering + multiple learning styles, we aim to engage all members, whether they + are beginners or advanced developers. To keep our members motivated, + we'll implement progress tracking features that allow users to + monitor their advancement through courses and tutorials. This will + help them set goals and celebrate their achievements. +

+

+ If you're ready to make a difference and help us build CodeDevils + Academy, we invite you to apply. Let's create a premier learning + platform that reflects our commitment to education and community. Your + journey with CodeDevils starts here—let's make something great! +

+ , +
+
+
+

Current Project Goals

+
+
+
    +
  • Implement a content player component
  • +
  • Create user authentication systems
  • +
  • Finalize front end and admin panel
  • +
+ +
+
, +
+ + {getSVG({ svgID: 3 })} + + + {getSVG({ svgID: 2 })} + + + {getSVG({ svgID: 4 })} + +
, +
+ +
, +
+ project image + project image + project image +
, +
+
+
+

Project Team

+
+
+ Project Leader's photo +
+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "name", + })} +

+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "bio", + })} +

+
+
+
+ {projectTeams[projectID][1] ? ( + projectTeams[projectID].map(([member, link], index) => + index > 0 ? ( +
+ {link ? ( + + {member} + + ) : ( +

{member}

+ )} +
+ ) : ( + <> + ) + ) + ) : ( <> -

- This is the codedevils.org project; the website you're currently looking at. With many features - planned, and with an active rebrand going on, we'll need some talented hands to keep this ship running. - This is considered one of our core projects, meaning it is essential to the CodeDevils brand and - identity. We build our websites in Next.js and Typescript. Next.js is a powerful framework for - JavaScript/TypeScript that handles frontend and backend deployments. One primary focus of the - website's design is to keep the code maintainable, manageable, and readable. We will not be building - web pages that are simply hard-coded in HTML, but are dynamic and rely on backend data structures to - function. -

- We have a lot of fun features planned, many of them are just to show off our skills as programmers, - but we're here to advertise our organization and put our best foot forward to outside companies, - prospective contributors, and to the university we stand behind. Our project is dedicated not to - being flashy, but to being robust and respectable to the community at large. We aim to create a - website that is not just functional but also resonates with our audience, reflecting our dedication - to quality and professionalism. -

- We're seeking contributors who share our passion for technology and community. Whether you're a - seasoned developer, a creative designer, or even someone wanting to dip their toes into web design - and learn from us, you're in the right place. If you're ready to make a meaningful contribution, - we invite you to join us. Whether you can dedicate a few hours or want to take on a larger role, - your efforts will help shape the future of CodeDevils.org. This is more than just coding; it's - about building a resource that empowers our community and showcases our commitment to excellence. -

- Let's work together to create a website that embodies the spirit of CodeDevils—dynamic, robust, - and community-focused. Your journey with us starts here. Let's make it remarkable! -

+
+

No contributors just yet. Be the first to apply!

- ), ( -
-
-
-

Current Project Goals

-
-
-
    -
  • Add and improve features using service API's
  • -
  • Redesign the website's colors and design to align with CodeDevils rebranding
  • -
  • Rewrite and redesign the website's content
  • -
- -
-
- ), ( -
- - { getSVG({ svgID: 3 }) } - - - { getSVG({ svgID: 2 }) } - - - { getSVG({ svgID: 4 }) } - -
- ), ( -
-
GitHub Activity
-
-
- ), ( -
- project image - project image - project image -
- ), ( -
-
-
-

Project Team

-
-
- Project Leader's photo -
-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'name'}) }

-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'bio'}) }

-
-
-
- { projectTeams[projectID][1] ? - ( projectTeams[projectID].map(([member, link], index) => ( - index > 0 ? ( -
- { link ? {member} - :

{member}

} -
) : <> - ))) : <>

No contributors just yet. Be the first to apply!

} -
-
- ), - ( - - ) - ], + )} +
+
, + , + ], - gamedev: [( -
-

CodeDevils GameDev

-
- -
-
- Join the Project! -
-
- ), ( -

A series of Game Development projects to implement in various places around CodeDevils!

- ), ( - <> -

- Do you want to learn more about game development? Then the CD-GameDev project is for you. - We have decided to use the Rust programming language, a growingly popular, high-performance, - multi-purpose programming language that is sure to be a great tool to add to your skill set. - And although most games tend to use object oriented programming, we have opted to use a different - paradigm that is also growing more popular in the game dev world; Entity Component System (ECS). - This paradigm allows for major performance gains in compute heavy games, and also offers a different - way to think about objects in our game by separating data and systems. For this endeavor, we will be - using the Bevy ECS framework, one of the best on the market. -

- One of the main drivers behind the decision of using Rust + Bevy, was that it is open-source, and - is turtles all the way down. In other words, everything is Rust from top to bottom, allowing us - to delve deep into how the Bevy ECS framework, and all its accompanying tools, work under the hood. - We want to teach you about all the components that goes into creating and running a game, and traditional - game engines usually abstract all those details away. But that is not to say that Bevy is not easy to use. - After some practice, we believe you'll find that it is incredibly ergonomic and user friendly, thanks to - the amazing engineers contributing to the Bevy project. -

- The game itself will be inspired from the - - metroidvania genre + website: [ +

, +

+ Our rapidly-evolving website. New features and tools are constantly + being added! +

, + <> +

+ This is the codedevils.org project; the website you're currently + looking at. With many features planned, and with an active rebrand + going on, we'll need some talented hands to keep this ship + running. This is considered one of our core projects, meaning it is + essential to the CodeDevils brand and identity. We build our websites + in Next.js and Typescript. Next.js is a powerful framework for + JavaScript/TypeScript that handles frontend and backend deployments. + One primary focus of the website's design is to keep the code + maintainable, manageable, and readable. We will not be building web + pages that are simply hard-coded in HTML, but are dynamic and rely on + backend data structures to function. +

+

+ We have a lot of fun features planned, many of them are just to show + off our skills as programmers, but we're here to advertise our + organization and put our best foot forward to outside companies, + prospective contributors, and to the university we stand behind. Our + project is dedicated not to being flashy, but to being robust and + respectable to the community at large. We aim to create a website that + is not just functional but also resonates with our audience, + reflecting our dedication to quality and professionalism. +

+

+ We're seeking contributors who share our passion for technology + and community. Whether you're a seasoned developer, a creative + designer, or even someone wanting to dip their toes into web design + and learn from us, you're in the right place. If you're + ready to make a meaningful contribution, we invite you to join us. + Whether you can dedicate a few hours or want to take on a larger role, + your efforts will help shape the future of CodeDevils.org. This is + more than just coding; it's about building a resource that + empowers our community and showcases our commitment to excellence. +

+

+ Let's work together to create a website that embodies the spirit + of CodeDevils—dynamic, robust, and community-focused. Your journey + with us starts here. Let's make it remarkable! +

+ , +
+
+
+

Current Project Goals

+
+
+
    +
  • Add and improve features using service API's
  • +
  • + Redesign the website's colors and design to align with + CodeDevils rebranding +
  • +
  • Rewrite and redesign the website's content
  • +
+ +
+
, +
+ + {getSVG({ svgID: 3 })} + + + {getSVG({ svgID: 2 })} + + + {getSVG({ svgID: 4 })} + +
, +
+ +
, +
+ project image + project image + project image +
, +
+
+
+

Project Team

+
+
+ Project Leader's photo +
+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "name", + })} +

+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "bio", + })} +

+
+
+
+ {projectTeams[projectID][1] ? ( + projectTeams[projectID].map(([member, link], index) => + index > 0 ? ( +
+ {link ? ( + + {member} - , but might take elements from other genres as - well. It will be a 2D, side-view game, with a pixel art style. The features and systems we will implement - will evolve over time as we prototype and progress together. By joining this project, you won't just be - coding what we tell you to code, we also want you to be part of the discussion about where the game is headed - in all of its aspect, would it be features and systems, or story and lore. -

- The journey will be a challenging one, but will be very rewarding as well. You might have to learn quite - a few things before you can get started contributing, especially if you never coded in Rust or are new - to game development, but we will be there every step of the way, and we have curated a selection of - resources to help you get started using Rust and Bevy, so that you can start your game dev journey - with us as fast as possible. From things like how to handle asset loading, animations, handling audio, - to how game handles physics and graphics rendering, you'll come out of this project with a greater - understanding of all that comes into play when creating games. -

- - ), ( -
-
-
-

Current Project Goals

-
-
-
    -
  • Create systems and plugins to facilitate the creation of the game
  • -
  • Program and integrate multiple gameplay systems together
  • -
  • Publish the game on a game distribution platform
  • -
- -
-
- ), ( -
- - { getSVG({ svgID: 0 }) } - - - { getSVG({ svgID: 1 }) } - -
- ), ( -
-
GitHub Activity
-
-
- ), ( -
- project image - project image - project image -
- ), ( -
-
-
-

Project Team

-
-
- Project Leader's photo -
-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'name'}) }

-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'bio'}) }

-
-
-
- { projectTeams[projectID][1] ? - ( projectTeams[projectID].map(([member, link], index) => ( - index > 0 ? ( -
- { link ? {member} - :

{member}

} -
) : <> - ))) : <>

No contributors just yet. Be the first to apply!

} -
-
- ), - ( - - ) - ], - - mobile: [( -
-

CodeDevils Mobile

-
- -
-
- Join the Project! -
-
- ), ( -

Our Mobile app! Built for iOS and Android, helps contributors keep up to date with projects and keeps members aware of events!

- ), ( + ) : ( +

{member}

+ )} +
+ ) : ( + <> + ) + ) + ) : ( <> -

- Welcome to the CodeDevils Mobile project page! As part of our commitment to fostering a community of - developers, we are excited to announce the development of our mobile app, CodeDevils Mobile. This project - is designed to create a welcoming space for anyone interested in mobile development, whether you're a - beginner looking to learn or an experienced developer eager to share your knowledge. -

- With CodeDevils Mobile, our vision is to build an inclusive platform that encourages exploration and - collaboration in the world of mobile app development. We recognize that mobile technology is an integral - part of our lives, and we want to provide a space where members can come together to learn, share ideas, - and showcase their projects. By utilizing Dart/Flutter, Swift, and Kotlin, we aim to create a versatile - application that caters to both iOS and Android platforms. -

- CodeDevils Mobile, in its current vision, will serve primarily as a community platform for CodeDevils - events, Academy content, and have a contributor platform to stay up to date with the latest project - announcements and changes. Additional features will come as the project unfolds. We have a lot of - excitement for this project and we're very excited to see what it becomes. -

- If you have a particular interest or passion for mobile development, we encourage you to get started - early on this project and make your mark! Your influence on the direction and execution of this project - will be invaluable to both your career and to the project as a whole. -

+
+

No contributors just yet. Be the first to apply!

- ), ( -
-
-
-

Current Project Goals

-
-
-
    -
  • Create systems and plugins to facilitate the creation of the game
  • -
  • Program and integrate multiple gameplay systems together
  • -
  • Publish the game on a game distribution platform
  • -
- -
-
- ), ( -
- - { getSVG({ svgID: 5 }) } - - - { getSVG({ svgID: 6 }) } - - - { getSVG({ svgID: 7 }) } - - - { getSVG({ svgID: 8 }) } - -
- ), ( -
-
GitHub Activity
-
-
- ), ( -
- project image - project image - project image -
- ), ( -
-
-
-

Project Team

-
-
- Project Leader's photo -
-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'name'}) }

-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'bio'}) }

-
-
-
- { projectTeams[projectID][1] ? - ( projectTeams[projectID].map(([member, link], index) => ( - index > 0 ? ( -
- { link ? {member} - :

{member}

} -
) : <> - ))) :

No contributors just yet. Be the first to apply!

} -
-
- ), - ( - - ) - ], + )} +
+
, + , + ], - scraper: [( -
-

CodeDevils Scraper

-
- -
-
- Join the Project! -
-
- ), ( -

A job post scraping tool built to keep our members up to date with the latest developments in the job market!

- ), ( + gamedev: [ +
+

CodeDevils GameDev

+
+ +
+
+ Join the Project! +
+
, +

+ A series of Game Development projects to implement in various places + around CodeDevils! +

, + <> +

+ Do you want to learn more about game development? Then the CD-GameDev + project is for you. We have decided to use the Rust programming + language, a growingly popular, high-performance, multi-purpose + programming language that is sure to be a great tool to add to your + skill set. And although most games tend to use object oriented + programming, we have opted to use a different paradigm that is also + growing more popular in the game dev world; Entity Component System + (ECS). This paradigm allows for major performance gains in compute + heavy games, and also offers a different way to think about objects in + our game by separating data and systems. For this endeavor, we will be + using the Bevy ECS framework, one of the best on the market. +

+

+ One of the main drivers behind the decision of using Rust + Bevy, was + that it is open-source, and is turtles all the way down. In other + words, everything is Rust from top to bottom, allowing us to delve + deep into how the Bevy ECS framework, and all its accompanying tools, + work under the hood. We want to teach you about all the components + that goes into creating and running a game, and traditional game + engines usually abstract all those details away. But that is not to + say that Bevy is not easy to use. After some practice, we believe + you'll find that it is incredibly ergonomic and user friendly, + thanks to the amazing engineers contributing to the Bevy project. +

+

+ The game itself will be inspired from the{" "} + + + metroidvania genre + + + , but might take elements from other genres as well. It will be a 2D, + side-view game, with a pixel art style. The features and systems we + will implement will evolve over time as we prototype and progress + together. By joining this project, you won't just be coding what + we tell you to code, we also want you to be part of the discussion + about where the game is headed in all of its aspect, would it be + features and systems, or story and lore. +

+

+ The journey will be a challenging one, but will be very rewarding as + well. You might have to learn quite a few things before you can get + started contributing, especially if you never coded in Rust or are new + to game development, but we will be there every step of the way, and + we have curated a selection of resources to help you get started using + Rust and Bevy, so that you can start your game dev journey with us as + fast as possible. From things like how to handle asset loading, + animations, handling audio, to how game handles physics and graphics + rendering, you'll come out of this project with a greater + understanding of all that comes into play when creating games. +

+ , +
+
+
+

Current Project Goals

+
+
+
    +
  • + Create systems and plugins to facilitate the creation of the game +
  • +
  • Program and integrate multiple gameplay systems together
  • +
  • Publish the game on a game distribution platform
  • +
+ +
+
, +
+ + {getSVG({ svgID: 0 })} + + + {getSVG({ svgID: 1 })} + +
, +
+ {/* Game Developer */} + +
, +
+ project image + project image + project image +
, +
+
+
+

Project Team

+
+
+ Project Leader's photo +
+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "name", + })} +

+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "bio", + })} +

+
+
+
+ {projectTeams[projectID][1] ? ( + projectTeams[projectID].map(([member, link], index) => + index > 0 ? ( +
+ {link ? ( + + {member} + + ) : ( +

{member}

+ )} +
+ ) : ( + <> + ) + ) + ) : ( <> -

- CD-Scraper is a project designed to teach the fundamentals of web scaping and the tools to make that happen. - The goal is to design a system for scraping job posting from various websites and serving the collected data - through an API build using SpringBoot. As this project is going to be based fully in Java, it is friendly to - those that are just starting off their programming journey. -

- Throughout this project, contributors will have the opportunity to collaborate with peers, share insights, - and learn from each other. This team-oriented approach fosters a supportive environment where beginners can - ask questions and gain confidence in their coding abilities. You'll not only develop your technical skills - but also engage in problem-solving and critical thinking as you navigate the challenges of web scraping - together. -

- Each contributor will be responsible for implementing a script to scrape different job posting websites - (i.e. Indeed, LinkedIn). Through this process, you will, hopefully, have a grasp on the some fundamental - HTML parsing. -

- In this project, you will NOT be working with APIs or any web related technology. Those will be managed - by staff to ensure this project is easy to pick up for those new to programming, but wanting to contribute - to a more complex project outside of school assignments. Also, you will NOT be working with any data and - their persistence (i.e. databases). -

- If you want to apply what you have learned about Java in class into a project that is more tangible, - join our beginner-friendly project by filling out our application. Your project lead will reach out to you - within the week. Let's learn to code by building something meaningful and fun! -

+
+

No contributors just yet. Be the first to apply!

- ), ( -
-
-
-

Current Project Goals

-
-
-
    -
  • Learn about web scraping
  • -
  • Take Java skills from school and implement them into a tangible project
  • -
  • Gain confidence to start your own personal projects
  • -
- -
-
- ), ( -
- - { getSVG({ svgID: 9 }) } - -
- ), ( -
-
GitHub Activity
-
-
- ), ( -
- project image - project image - project image -
- ), ( -
-
-
-

Project Team

-
-
- Project Leader's photo -
-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'name'}) }

-

{ getMember({memberID: projectTeams[projectID][0]?.[0] as MemberID, memberData: 'bio'}) }

-
-
-
- { projectTeams[projectID][1] ? - ( projectTeams[projectID].map(([member, link], index) => ( - index > 0 ? ( -
- { link ? {member} - :

{member}

} -
) : <> - ))) :

No contributors just yet. Be the first to apply!

} -
-
- ), - ( - - ) - ], - - } + )} +
+
, + , + ], - if (!(projectID in content)) { throw new Error('Invalid Project ID'); } - if (contentID < 0 || contentID >= content[projectID].length) { throw new Error('Invalid Content ID'); } + mobile: [ +
+

CodeDevils Mobile

+
+ +
+
+ Join the Project! +
+
, +

+ Our Mobile app! Built for iOS and Android, helps contributors keep up to + date with projects and keeps members aware of events! +

, + <> +

+ Welcome to the CodeDevils Mobile project page! As part of our + commitment to fostering a community of developers, we are excited to + announce the development of our mobile app, CodeDevils Mobile. This + project is designed to create a welcoming space for anyone interested + in mobile development, whether you're a beginner looking to learn + or an experienced developer eager to share your knowledge. +

+

+ With CodeDevils Mobile, our vision is to build an inclusive platform + that encourages exploration and collaboration in the world of mobile + app development. We recognize that mobile technology is an integral + part of our lives, and we want to provide a space where members can + come together to learn, share ideas, and showcase their projects. By + utilizing Dart/Flutter, Swift, and Kotlin, we aim to create a + versatile application that caters to both iOS and Android platforms. +

+

+ CodeDevils Mobile, in its current vision, will serve primarily as a + community platform for CodeDevils events, Academy content, and have a + contributor platform to stay up to date with the latest project + announcements and changes. Additional features will come as the + project unfolds. We have a lot of excitement for this project and + we're very excited to see what it becomes. +

+

+ If you have a particular interest or passion for mobile development, + we encourage you to get started early on this project and make your + mark! Your influence on the direction and execution of this project + will be invaluable to both your career and to the project as a whole. +

+ , +
+
+
+

Current Project Goals

+
+
+
    +
  • + Create systems and plugins to facilitate the creation of the game +
  • +
  • Program and integrate multiple gameplay systems together
  • +
  • Publish the game on a game distribution platform
  • +
+ +
+
, +
+ + {getSVG({ svgID: 5 })} + + + {getSVG({ svgID: 6 })} + + + {getSVG({ svgID: 7 })} + + + {getSVG({ svgID: 8 })} + +
, +
+ +
, +
+ project image + project image + project image +
, +
+
+
+

Project Team

+
+
+ Project Leader's photo +
+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "name", + })} +

+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "bio", + })} +

+
+
+
+ {projectTeams[projectID][1] ? ( + projectTeams[projectID].map(([member, link], index) => + index > 0 ? ( +
+ {link ? ( + + {member} + + ) : ( +

{member}

+ )} +
+ ) : ( + <> + ) + ) + ) : ( +

No contributors just yet. Be the first to apply!

+ )} +
+
, + , + ], + scraper: [ +
+

CodeDevils Scraper

+
+ +
+
+ Join the Project! +
+
, +

+ A job post scraping tool built to keep our members up to date with the + latest developments in the job market! +

, + <> +

+ CD-Scraper is a project designed to teach the fundamentals of web + scaping and the tools to make that happen. The goal is to design a + system for scraping job posting from various websites and serving the + collected data through an API build using SpringBoot. As this project + is going to be based fully in Java, it is friendly to those that are + just starting off their programming journey. +

+

+ Throughout this project, contributors will have the opportunity to + collaborate with peers, share insights, and learn from each other. + This team-oriented approach fosters a supportive environment where + beginners can ask questions and gain confidence in their coding + abilities. You'll not only develop your technical skills but also + engage in problem-solving and critical thinking as you navigate the + challenges of web scraping together. +

+

+ Each contributor will be responsible for implementing a script to + scrape different job posting websites (i.e. Indeed, LinkedIn). Through + this process, you will, hopefully, have a grasp on the some + fundamental HTML parsing. +

+

+ In this project, you will NOT be working with APIs or any web related + technology. Those will be managed by staff to ensure this project is + easy to pick up for those new to programming, but wanting to + contribute to a more complex project outside of school assignments. + Also, you will NOT be working with any data and their persistence + (i.e. databases). +

+

+ If you want to apply what you have learned about Java in class into a + project that is more tangible, join our beginner-friendly project by + filling out our application. Your project lead will reach out to you + within the week. Let's learn to code by building something + meaningful and fun! +

+ , +
+
+
+

Current Project Goals

+
+
+
    +
  • Learn about web scraping
  • +
  • + Take Java skills from school and implement them into a tangible + project +
  • +
  • Gain confidence to start your own personal projects
  • +
+ +
+
, +
+ + {getSVG({ svgID: 9 })} + +
, +
+ +
, +
+ project image + project image + project image +
, +
+
+
+

Project Team

+
+
+ Project Leader's photo +
+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "name", + })} +

+

+ {getMember({ + memberID: projectTeams[projectID][0]?.[0] as MemberID, + memberData: "bio", + })} +

+
+
+
+ {projectTeams[projectID][1] ? ( + projectTeams[projectID].map(([member, link], index) => + index > 0 ? ( +
+ {link ? ( + + {member} + + ) : ( +

{member}

+ )} +
+ ) : ( + <> + ) + ) + ) : ( +

No contributors just yet. Be the first to apply!

+ )} +
+
, + , + ], + }; - return (content[projectID][contentID]); + if (!(projectID in content)) { + throw new Error("Invalid Project ID"); + } + if (contentID < 0 || contentID >= content[projectID].length) { + throw new Error("Invalid Content ID"); + } -} + return content[projectID][contentID]; +}; -const getContent = ({projectID, contentID, pageID}:{ - projectID: ProjectID | 'all', - contentID: number - pageID: string +const getContent = ({ + projectID, + contentID, + pageID, +}: { + projectID: ProjectID | "all"; + contentID: number; + pageID: string; }) => { - if (projectID === 'all') { return projectIDs.map(id => processContent({projectID: id, contentID, pageID})); } - return processContent({projectID, contentID, pageID}); -} + if (projectID === "all") { + return projectIDs.map((id) => + processContent({ projectID: id, contentID, pageID }) + ); + } + return processContent({ projectID, contentID, pageID }); +}; export default getContent; diff --git a/src/app/(main)/(routes)/projects/page.tsx b/src/app/(main)/(routes)/projects/page.tsx index 37de512..758f4ba 100644 --- a/src/app/(main)/(routes)/projects/page.tsx +++ b/src/app/(main)/(routes)/projects/page.tsx @@ -1,126 +1,158 @@ -import React from 'react'; -import type { Metadata } from 'next'; -import Image from 'next/image'; +import React from "react"; +import type { Metadata } from "next"; +import Image from "next/image"; -import './project-styles.css'; -import { Section } from 'src/app/components/ui/ContentWrappers'; -import { ExternalLink, InternalLink } from 'src/app/components/ui/buttons'; -import { socialLinks } from 'src/app/utils/staticdata'; -import getContent, { projectIDs } from './content'; -import { Backpack, ChevronRight, FileOutput, Gamepad2, PanelsTopLeft, Smartphone, Square } from 'lucide-react'; -import Link from 'next/link'; +import "./project-styles.css"; +import { Section } from "src/app/components/ui/ContentWrappers"; +import { ExternalLink, InternalLink } from "src/app/components/ui/buttons"; +import { socialLinks } from "src/app/utils/staticdata"; +import getContent, { projectIDs } from "./content"; +import { + Backpack, + ChevronRight, + FileOutput, + Gamepad2, + PanelsTopLeft, + Smartphone, + Square, +} from "lucide-react"; +import Link from "next/link"; +import GitMonitor from "../../../components/Projects/GitMonitor"; export const metadata: Metadata = { - title: 'Projects Hub', + title: "Projects Hub", description: - 'Discover open source projects at CodeDevils and learn how to get involved. Collaborate with fellow developers, gain real-world experience, and make a meaningful impact through open source contributions.', + "Discover open source projects at CodeDevils and learn how to get involved. Collaborate with fellow developers, gain real-world experience, and make a meaningful impact through open source contributions.", openGraph: { - title: 'Projects Hub', + title: "Projects Hub", description: - 'Discover open source projects at CodeDevils and learn how to get involved. Collaborate with fellow developers, gain real-world experience, and make a meaningful impact through open source contributions.', + "Discover open source projects at CodeDevils and learn how to get involved. Collaborate with fellow developers, gain real-world experience, and make a meaningful impact through open source contributions.", }, }; const projectsPage = () => { const { discord } = socialLinks; - const projectDescs = getContent({projectID: 'all', contentID: 1, pageID: "project-hub" }) as React.ReactNode[]; - const projectTitles = getContent({projectID: 'all', contentID: 0, pageID: "project-hub" }) as React.ReactNode[]; - + const projectDescs = getContent({ + projectID: "all", + contentID: 1, + pageID: "project-hub", + }) as React.ReactNode[]; + const projectTitles = getContent({ + projectID: "all", + contentID: 0, + pageID: "project-hub", + }) as React.ReactNode[]; return ( <> -
+
-
-

+
+

Our members are foundational to our projects

-

- Here are their achievements. -

+

Here are their achievements.

- {/* Card */} -
- -
-
-
Git Activity
-
-
    -
  • -
    -

    - No Activity yet...

    -
  • -
-
+ {/* Card */} +
+
+
+
-
-

Contributor Leaderboard

-

Coming Soon...

- -

Be a part of the Project!

- - -
-

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

+ cursor-pointer shadow-md shadow-zinc-500 mt-48 scale-75" + > +

+ Be a part of the Project! +

+ +
+

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

-
-

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

- -

Under Construction

+
+

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

+ +

+ Under Construction +

- -
- +
- -
-
- -

+
+
+

Contribute to our open-source projects

-

+

Get involved with our open-source projects to build your portfolio up for future job opportunities. We have a variety of projects that you can contribute to help you grow your skills and gain real-world @@ -128,106 +160,109 @@ const projectsPage = () => {

- {/* List of Project Titles. Dynamically generated. */} -
    - - { projectDescs ? projectDescs.map((description, index) => ( -
  • -
    -
    - - - {projectTitles[index]} - - {description} -
    - -
    -
  • - )) : <> } - - + rounded-full" + /> + + )) + ) : ( + <> + )}
-
-
-

Get Started

-

+
+

Get Started

+

Want to contribute to our projects, but you're new?

-

+

No worries! We have a guide to help you get started with contributing to our projects. We welcome all skill levels to contribute to our projects.

-
+
GitHub -
    -
  • -
    -

    +
      +
    • +
      +

      Step 1: Join our Discord Server

      -

      +

      Join our online community to stay up to date with our projects and be involved in our discussions.

      Join our Discord -

      +

      to get involved with our community, projects, and access our resources.

    • -
    • -
      -

      +
    • +
      +

      Step 2: Talk to the project's lead

      -

      +

      For whichever project you are interested in, talk to the lead of the project to learn how to get started.

      - + Meet our project leads -

      +

      to learn more about our projects and how to get started with them.

    • -
    • -
      -

      +
    • +
      +

      Step 3: Read our Project Documentation

      -

      +

      Each project has its own documentation to help you get started with contributing. Make sure to read it before you start.

      - + View our Documentation -

      +

      to understand our projects and get started with contributing.

      @@ -235,9 +270,6 @@ const projectsPage = () => {

- - - ); }; diff --git a/src/app/(main)/(routes)/projects/project-styles.css b/src/app/(main)/(routes)/projects/project-styles.css index 59de742..7a4d307 100644 --- a/src/app/(main)/(routes)/projects/project-styles.css +++ b/src/app/(main)/(routes)/projects/project-styles.css @@ -1,218 +1,186 @@ - #project-title.project-page { - @apply flex flex-col lg:flex-row relative w-full items-center mb-12 justify-center lg:justify-start; + @apply flex flex-col lg:flex-row relative w-full items-center mb-12 justify-center lg:justify-start; } #project-title.project-page h1 { - @apply font-black + @apply font-black underline underline-offset-2 transition-all duration-100 hover:underline-offset-4 md:text-5xl text-3xl; } #project-title.project-page div:nth-of-type(1) > *:first-child { - @apply text-asumaroon-900 transition-transform duration-200 hover:rotate-[-20deg] + @apply text-asumaroon-900 transition-transform duration-200 hover:rotate-[-20deg] w-16 h-16 ml-4 lg:block hidden; } #project-title.project-page div:nth-of-type(2) { - @apply lg:ml-auto md:scale-100 scale-75 mt-4 lg:mt-0; + @apply lg:ml-auto md:scale-100 scale-75 mt-4 lg:mt-0; } - - - #project-text.project-page { - @apply text-lg md:text-xl indent-8 mb-4; + @apply text-lg md:text-xl indent-8 mb-4; } - - - #project-stack.project-page { - @apply flex flex-col justify-start items-center gap-2 p-4 rounded-2xl mr-4 float-left + @apply flex flex-col justify-start items-center gap-2 p-4 rounded-2xl mr-4 float-left bg-gradient-to-b from-zinc-200 to-zinc-100 shadow-inner shadow-zinc-300 md:w-56 w-32; } #project-stack.project-page svg { - @apply w-32 h-32 hover:scale-110 transition-transform duration-200 fill-asumaroon-800; + @apply w-32 h-32 hover:scale-110 transition-transform duration-200 fill-asumaroon-800; } - - - #git-monitor { - @apply lg:flex flex-col hidden text-zinc-100 float-right ml-4 -mr-4 -mt-2 mb-4; -} - -#git-monitor div:nth-of-type(1) { - @apply flex items-center justify-center h-[80px] w-[320px] rounded-t-lg - font-semibold text-2xl shadow-xl shadow-slate-700 - bg-gradient-to-r from-slate-950 to-zinc-800; + @apply lg:flex flex-col hidden text-zinc-100 float-right ml-4 mr-4 mb-4; + position: relative; + top: -5rem; } -#git-monitor div:nth-of-type(2) { - @apply h-[600px] w-[320px] rounded-b-lg - shadow-lg shadow-slate-500 - bg-gradient-to-br from-slate-800 to-slate-900; +#git-monitor > div { + @apply w-[320px] h-[680px]; } - - - #project-images.project-page { - @apply grid gap-4 mt-12 + @apply grid gap-4 mt-12 md:grid-cols-3 md:grid-rows-1 md:h-64 grid-cols-1 grid-rows-3; } #project-images.project-page * { - @apply h-full w-full object-cover; + @apply h-full w-full object-cover; } - - - #project-goals.project-page > div:nth-child(1) { - @apply flex flex-col items-center mt-12; + @apply flex flex-col items-center mt-12; } #project-goals.project-page > div:nth-child(1) div { - @apply w-full h-0.5 md:bg-asumaroon bg-transparent; + @apply w-full h-0.5 md:bg-asumaroon bg-transparent; } #project-goals.project-page h2 { - @apply text-3xl font-bold mb-5 -mt-6 bg-white px-4; + @apply text-3xl font-bold mb-5 -mt-6 bg-white px-4; } #project-goals.project-page div ul { - @apply flex flex-col gap-2 md:ml-8 my-2 font-semibold mb-12 text-asumaroon-950 w-full + @apply flex flex-col gap-2 md:ml-8 my-2 font-semibold mb-12 text-asumaroon-950 w-full md:text-xl text-lg; } #project-goals.project-page div ul li { - @apply px-4 py-1 rounded-md cursor-default + @apply px-4 py-1 rounded-md cursor-default md:transition-transform md:hover:translate-x-4 md:duration-200 md:bg-gradient-to-r md:from-asumaroon-200 md:to-stone-100 md:w-[80%] bg-zinc-100 w-full; } #project-goals.project-page > div:nth-child(2) { - @apply flex flex-row w-full relative + @apply flex flex-row w-full relative; } #project-goals.project-page > div:nth-child(2) > *:nth-child(2) { - @apply absolute h-full w-40 p-4 right-4 rounded-2xl text-zinc-300 bg-zinc-100 hidden xl:block + @apply absolute h-full w-40 p-4 right-4 rounded-2xl text-zinc-300 bg-zinc-100 hidden xl:block; } - - - #project-team.project-page > div:nth-of-type(1) { - @apply flex flex-col items-center mt-16; + @apply flex flex-col items-center mt-16; } #project-team.project-page > div:nth-of-type(1) div { - @apply w-full h-0.5 md:bg-asumaroon bg-transparent; + @apply w-full h-0.5 md:bg-asumaroon bg-transparent; } #project-team.project-page > div:nth-of-type(1) h2 { - @apply text-3xl font-bold mb-5 md:-mt-6 bg-white px-4; + @apply text-3xl font-bold mb-5 md:-mt-6 bg-white px-4; } #project-team.project-page > div:nth-of-type(2) { - @apply flex w-full bg-zinc-100 p-4 rounded-t-xl mt-4 + @apply flex w-full bg-zinc-100 p-4 rounded-t-xl mt-4 md:flex-row flex-col items-center md:items-start; } #project-team.project-page > div:nth-of-type(2) img { - @apply w-[240px] h-[240px] object-cover; + @apply w-[240px] h-[240px] object-cover; } #project-team.project-page > div:nth-of-type(2) div { - @apply flex flex-col w-full h-full + @apply flex flex-col w-full h-full md:items-start md:mt-0 md:gap-0 items-center mt-2 gap-2; } #project-team.project-page > div:nth-of-type(2) div h2 { - @apply bg-transparent text-2xl font-normal ml-4; + @apply bg-transparent text-2xl font-normal ml-4; } #project-team.project-page > div:nth-of-type(2) div p { - @apply text-lg font-light ml-4; + @apply text-lg font-light ml-4; } #project-team.project-page > div:nth-of-type(3) { - @apply grid text-center text-lg font-semibold py-8 mb-12 gap-2 px-2 + @apply grid text-center text-lg font-semibold py-8 mb-12 gap-2 px-2 bg-zinc-100 rounded-b-xl border-t-4 border-white md:grid-cols-3 grid-cols-2; } #project-team.project-page > div:nth-of-type(3) div { - @apply flex justify-center w-full; + @apply flex justify-center w-full; } #project-team.project-page > div:nth-of-type(3) div * { - @apply hover:rounded-xl w-64 + @apply hover:rounded-xl w-64 md:hover:bg-white md:bg-transparent bg-white rounded-xl; } #project-team.project-page > div:nth-of-type(3) div a { - @apply text-asumaroon underline + @apply text-asumaroon underline; } #project-team.project-page > div:nth-of-type(3) div p { - @apply cursor-default; + @apply cursor-default; } #project-team.project-page > div:nth-of-type(3) h3 { - @apply md:col-span-3 col-span-2 text-center; + @apply md:col-span-3 col-span-2 text-center; } - - #info-link { - @apply text-asumaroon hover:underline visited:text-asumaroon; + @apply text-asumaroon hover:underline visited:text-asumaroon; } - - - - - #project-title.project-hub { - @apply flex flex-row; + @apply flex flex-row; } #project-title.project-hub h1 { - @apply font-semibold text-asumaroon-900 order-2 cursor-pointer + @apply font-semibold text-asumaroon-900 order-2 cursor-pointer transition-all duration-100 hover:underline-offset-2 hover:underline md:text-5xl text-3xl; } #project-title.project-hub div:nth-of-type(1) > *:first-child { - @apply absolute text-asumaroon-800 transition-transform duration-200 hover:rotate-[-10deg] hover:scale-110 + @apply absolute text-asumaroon-800 transition-transform duration-200 hover:rotate-[-10deg] hover:scale-110 w-24 h-24 lg:block hidden order-1 left-8 top-6 cursor-pointer; } #project-title.project-hub div:nth-of-type(2) { - @apply hidden; + @apply hidden; } #project-description.project-hub { - @apply text-lg text-zinc-700 font-medium mt-2 ml-4 leading-tight; + @apply text-lg text-zinc-700 font-medium mt-2 ml-4 leading-tight; } #git-link { - @apply flex justify-center md:justify-end items-center mt-3 md:mt-[-77px] mb-10 w-full; + @apply flex justify-center md:justify-end items-center mt-3 md:mt-[-77px] mb-10 w-full; } #git-link a { - @apply text-lg font-light text-asumaroon-500 duration-300 hover:text-asumaroon-950 hover:font-medium underline flex items-center cursor-pointer; -} \ No newline at end of file + @apply text-lg font-light text-asumaroon-500 duration-300 hover:text-asumaroon-950 hover:font-medium underline flex items-center cursor-pointer; +} diff --git a/src/app/api/github/github-service.ts b/src/app/api/github/github-service.ts new file mode 100644 index 0000000..8c984f5 --- /dev/null +++ b/src/app/api/github/github-service.ts @@ -0,0 +1,275 @@ +// lib/github-service.ts +// Define types for GitHub API responses +export type Contributor = { + login: string; + avatar_url: string; + html_url: string; + contributions: number; +}; + +export type Commit = { + sha: string; + commit: { + message: string; + author: { + name: string; + date: string; + }; + }; + author: { + login: string; + avatar_url: string; + } | null; + html_url: string; +}; + +export type CommitActivity = { + username: string; + avatar: string; + message: string; + date: string; + repo: string; + url: string; + id: string; +}; + +export type ContributorStats = { + username: string; + avatar: string; + totalCommits: number; + repositories: { + [repo: string]: number; + }; +}; + +export type GitHubData = { + recentActivity: CommitActivity[]; + contributors: ContributorStats[]; + error?: string; +}; + +// Helper function to get auth headers +const getAuthHeaders = (): Record => { + // Server-side environment variable access + const token = process.env.GITHUB_TOKEN || ""; + + // Safe debug logging - only show if token exists and first/last few chars + if (token) { + const tokenPrefix = token.substring(0, 4); + const tokenSuffix = token.substring(token.length - 4); + console.log(`GitHub token loaded - starts with: ${tokenPrefix}... ends with: ...${tokenSuffix}`); + console.log(`Token length: ${token.length} characters`); + } else { + console.log("WARNING: No GitHub token found in environment variables"); + } + + // Always return a Record to satisfy TypeScript + const headers: Record = { + Accept: "application/vnd.github.v3+json", + }; + + // Conditionally add Authorization if token exists + if (token) { + headers["Authorization"] = `Bearer ${token}`; + } + + return headers; +}; + +// Helper function to format dates +const formatDate = (dateString: string): string => { + const date = new Date(dateString); + return new Intl.DateTimeFormat("en-US", { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }).format(date); +}; + +// Main function to fetch GitHub data +export async function fetchGitHubData(repos: string[]): Promise { + try { + const headers = getAuthHeaders(); + const usingAuth = headers.hasOwnProperty("Authorization"); + + const allCommits: CommitActivity[] = []; + const contributorMap: Record = {}; + + for (let i = 0; i < repos.length; i++) { + const repo = repos[i]; + + try { + // Log that we're fetching for this repo + console.log(`Fetching commits for ${repo}...`); + + // Fetch rate limit info first to confirm auth is working + const rateResponse = await fetch( + "https://api.github.com/rate_limit", + { headers } + ); + + if (rateResponse.ok) { + const rateData = await rateResponse.json(); + console.log(`GitHub API Rate Limits:`); + console.log(`- Remaining core requests: ${rateData.resources.core.remaining}/${rateData.resources.core.limit}`); + console.log(`- Reset time: ${new Date(rateData.resources.core.reset * 1000).toLocaleTimeString()}`); + + // Authenticated users get 5000 requests/hour, unauthenticated get 60/hour + if (rateData.resources.core.limit > 60) { + console.log("✅ Authentication confirmed - using higher rate limits"); + } else { + console.log("⚠️ WARNING: Using unauthenticated rate limits - token may be invalid"); + } + } + + // Fetch recent commits + console.log(`Making request to GitHub API: https://api.github.com/repos/${repo}/commits`); + console.log(`Using Authorization header: ${headers.Authorization ? 'Bearer [token]' : 'None'}`); + + // Test the token directly with a simple API call first + const userResponse = await fetch('https://api.github.com/user', { headers }); + if (userResponse.status === 401) { + console.error('GitHub token is invalid or expired. Please generate a new token.'); + console.error(`GitHub API response: ${userResponse.status} ${userResponse.statusText}`); + + // Try to get more details about the error + try { + const errorData = await userResponse.json(); + console.error('GitHub error details:', errorData); + } catch (e) { + console.error('Could not parse error response'); + } + + return { + recentActivity: [], + contributors: [], + error: `GitHub authentication failed: Your token is invalid or expired` + }; + } + + const commitsResponse = await fetch( + `https://api.github.com/repos/${repo}/commits?per_page=30`, + { headers } + ); + + // Handle rate limiting + if (commitsResponse.status === 403) { + return { + recentActivity: [], + contributors: [], + error: `GitHub API rate limit exceeded. Please try again later.` + }; + } + + if (!commitsResponse.ok) { + console.error( + `Failed to fetch commits for ${repo}: ${commitsResponse.status}` + ); + continue; // Skip to next repo + } + + const commits: Commit[] = await commitsResponse.json(); + + // Process commits + commits.forEach((commit) => { + // Make sure all required fields exist before adding the commit + if ( + commit.author?.login && + commit.author?.avatar_url && + commit.commit?.message && + commit.commit?.author?.date && + commit.html_url && + commit.sha + ) { + // Extract repository name safely + const repoParts = repo.split("/"); + const repoName = repoParts.length > 1 ? repoParts[1] : repo; + + // Add to recent activity - using commit.sha as a unique ID + allCommits.push({ + username: commit.author.login, + avatar: commit.author.avatar_url, + message: + commit.commit.message.split("\n")[0].substring(0, 60) + + (commit.commit.message.length > 60 ? "..." : ""), + date: formatDate(commit.commit.author.date), + repo: repoName, + url: commit.html_url, + id: commit.sha, + }); + + // Update contributor stats + const username = commit.author.login; + if (!contributorMap[username]) { + contributorMap[username] = { + username, + avatar: commit.author.avatar_url, + totalCommits: 0, + repositories: {}, + }; + } + contributorMap[username].totalCommits += 1; + contributorMap[username].repositories[repo] = + (contributorMap[username].repositories[repo] || 0) + 1; + } + }); + + // Fetch contributors for more accurate commit counts + const contributorsResponse = await fetch( + `https://api.github.com/repos/${repo}/contributors`, + { headers } + ); + + if (contributorsResponse.ok) { + const repoContributors: Contributor[] = + await contributorsResponse.json(); + + repoContributors.forEach((contributor) => { + const username = contributor.login; + if (!contributorMap[username]) { + contributorMap[username] = { + username, + avatar: contributor.avatar_url, + totalCommits: 0, + repositories: {}, + }; + } + // Update with more accurate count from GitHub + contributorMap[username].repositories[repo] = + contributor.contributions; + contributorMap[username].totalCommits = Object.values( + contributorMap[username].repositories + ).reduce((sum, count) => sum + count, 0); + }); + } + } catch (repoError) { + console.error(`Error fetching data for ${repo}:`, repoError); + // Continue with other repos even if one fails + } + } + + // Sort contributors by total commits + const sortedContributors = Object.values(contributorMap).sort( + (a, b) => b.totalCommits - a.totalCommits + ); + + // Sort activity by date (recent first) + const sortedActivity = allCommits + .sort( + (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() + ) + + return { + recentActivity: sortedActivity, + contributors: sortedContributors + }; + } catch (err) { + console.error("Error fetching GitHub data:", err); + return { + recentActivity: [], + contributors: [], + error: "Failed to fetch GitHub data. Please try again later." + }; + } +} \ No newline at end of file diff --git a/src/app/api/github/route.ts b/src/app/api/github/route.ts new file mode 100644 index 0000000..7a7aaa9 --- /dev/null +++ b/src/app/api/github/route.ts @@ -0,0 +1,45 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { fetchGitHubData } from '../../api/github/github-service'; + +export async function GET(request: NextRequest) { + console.log("GitHub API route called"); + + // Debug token presence + const hasToken = !!process.env.GITHUB_TOKEN; + console.log(`GitHub token exists: ${hasToken}`); + + // Get repos from URL query parameters + const url = new URL(request.url); + const reposParam = url.searchParams.get('repos'); + + if (!reposParam) { + console.log("Error: No repositories specified in request"); + return NextResponse.json( + { error: 'No repositories specified' }, + { status: 400 } + ); + } + + // Parse the repos parameter + const repos = reposParam.split(','); + console.log(`Fetching data for repositories: ${repos.join(', ')}`); + + // Fetch the GitHub data + console.time('GitHub API fetch duration'); + const data = await fetchGitHubData(repos); + console.timeEnd('GitHub API fetch duration'); + + // Log results summary without exposing sensitive data + console.log(`API results: ${data.recentActivity.length} activities, ${data.contributors.length} contributors, authenticated: ${data.usingAuthentication}, error: ${data.error ? 'yes' : 'no'}`); + + // Log repository status + if (data.repoStatuses && data.repoStatuses.length > 0) { + console.log("Repository status summary:"); + data.repoStatuses.forEach(status => { + console.log(`- ${status.name}: ${status.isAccessible ? 'OK' : `Error (${status.errorCode}): ${status.errorMessage}`}`); + }); + } + + // Return the data + return NextResponse.json(data); +} \ No newline at end of file diff --git a/src/app/components/Projects/GitMonitor.tsx b/src/app/components/Projects/GitMonitor.tsx index bee05b9..fe62bc6 100644 --- a/src/app/components/Projects/GitMonitor.tsx +++ b/src/app/components/Projects/GitMonitor.tsx @@ -1,16 +1,248 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import { + User, + GitCommit, + Activity, + AlertTriangle, + CheckCircle, +} from "lucide-react"; +import { + CommitActivity, + ContributorStats, +} from "../../api/github/github-service"; +const DEFAULT_REPOS = [ + "ASU-CodeDevils/CD-Academy", + "ASU-CodeDevils/codedevils.org", + "ASU-CodeDevils/CD-GameDev", + "ASU-CodeDevils/CD-Mobile", + "ASU-CodeDevils/scraper.codedevils.org", +]; - -const GitMonitor = ({className, project}:{ - className: string - project: number +const GitMonitor = ({ + className, + repos = DEFAULT_REPOS, + remove_selection = false, +}: { + className?: string; + repos?: string[]; + remove_selection?: boolean; }) => { + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [recentActivity, setRecentActivity] = useState([]); + const [contributors, setContributors] = useState([]); + const [selectedRepo, setSelectedRepo] = useState("all"); + const [usingAuth, setUsingAuth] = useState(false); + const [rateLimit, setRateLimit] = useState<{ + limit: number; + remaining: number; + reset: string; + } | null>(null); + const [showRepoStatus, setShowRepoStatus] = useState(false); + + // Function to fetch data from our API endpoint + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + setError(null); + + // Prepare the repositories parameter + const reposParam = repos.join(","); + + // Call our API endpoint + const response = await fetch( + `/api/github?repos=${encodeURIComponent(reposParam)}` + ); + const data = await response.json(); + + if (data.error) { + setError(data.error); + setLoading(false); + return; + } + + setRecentActivity(data.recentActivity || []); + setContributors(data.contributors || []); + + // Use the authentication status directly from the API response + setUsingAuth(data.usingAuthentication || false); + + // Set rate limit info if available + if (data.rateLimit) { + setRateLimit(data.rateLimit); + } - return ( -
+ setLoading(false); + } catch (err) { + setError("Failed to fetch GitHub data. Please try again later."); + setLoading(false); + console.error("Error fetching GitHub data:", err); + } + }; + fetchData(); + }, [repos]); + + // Filter activity based on selected repository + const filteredActivity = + selectedRepo === "all" + ? recentActivity + : recentActivity.filter((activity) => { + // Get the short name from the selected repo (e.g., "scraper.codedevils.org" from "ASU-CodeDevils/scraper.codedevils.org") + const selectedRepoShortName = selectedRepo.split("/")[1] || ""; + console.log( + `Activity repo: ${activity.repo}, Selected repo: ${selectedRepoShortName}` + ); + // Compare with the activity's repo field + return activity.repo === selectedRepoShortName; + }); + + return ( +
+ {/* IMPORTANT: Always keep the header div for CSS structure, but conditionally hide its contents */} +
+
+ Git Activity
- ) -} -export default GitMonitor \ No newline at end of file + +
+ +
+ {loading ? ( +
+
+

Fetching repository data...

+
+ ) : error ? ( +
+ +

{error}

+ {!usingAuth && ( +

+ Consider adding a GitHub token to increase rate limits. +

+ )} +
+ ) : filteredActivity.length === 0 ? ( +
+ +

No activity yet...

+ + {selectedRepo !== "all" && ( +
+

+ Selected repository: {selectedRepo} +

+

+ This repository might not have any recent activity or the + repository name might be different in the activity data. +

+

+ Repositories in activity data: +

+
+ {[...new Set(recentActivity.map((a) => a.repo))].map( + (repo) => ( + + {repo} + + ) + )} +
+
+ )} + + {!usingAuth && ( +

+ GitHub API rate limit may have been exceeded. +
+ Consider adding a GitHub token for higher limits. +

+ )} +
+ ) : ( + <> + {/* Repository selection info */} + {selectedRepo !== "all" && ( +
+ + Showing activity for:{" "} + + {selectedRepo.split("/")[1]} + + + + ({filteredActivity.length} activities) + +
+ )} + + {/* Recent Activity (with proper keys) */} +

+ Recent Activity +

+
    + {filteredActivity.map((activity) => ( +
  • + {activity.username} +
    +
    + + {activity.username} + + + {activity.repo} • {activity.date} + +
    +

    + {activity.message} +

    +
    +
  • + ))} +
+ + )} +
+
+ ); +}; + +export default GitMonitor; From 6eed856c493753dc0bfc47c9b380fa15ecd9f67a Mon Sep 17 00:00:00 2001 From: Korbin101lee Date: Sat, 10 May 2025 14:43:35 -0700 Subject: [PATCH 2/2] cleaned up the code and added images to the domains to use Image for the github image API. --- next.config.mjs | 6 +++++- src/app/(main)/(routes)/projects/page.tsx | 2 +- src/app/components/Projects/GitMonitor.tsx | 11 +++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index 4678774..9c9d64b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,8 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + images: { + domains: ["avatars.githubusercontent.com"], + }, +}; export default nextConfig; diff --git a/src/app/(main)/(routes)/projects/page.tsx b/src/app/(main)/(routes)/projects/page.tsx index 758f4ba..f6b3958 100644 --- a/src/app/(main)/(routes)/projects/page.tsx +++ b/src/app/(main)/(routes)/projects/page.tsx @@ -58,7 +58,7 @@ const projectsPage = () => {
- +
{ - // Get the short name from the selected repo (e.g., "scraper.codedevils.org" from "ASU-CodeDevils/scraper.codedevils.org") const selectedRepoShortName = selectedRepo.split("/")[1] || ""; - console.log( - `Activity repo: ${activity.repo}, Selected repo: ${selectedRepoShortName}` - ); // Compare with the activity's repo field return activity.repo === selectedRepoShortName; }); return (
- {/* IMPORTANT: Always keep the header div for CSS structure, but conditionally hide its contents */}
- {activity.username}