diff --git a/.gitignore b/.gitignore index 9f67afc6..adc80bcb 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ yarn-error.log* # vscode .vscode + +#Jetbrians +.idea \ No newline at end of file diff --git a/README.md b/README.md index fed81ba3..0d93925b 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,11 @@

HackKit

Feature-packed Hackathon Managment Software That Just Works.


-

Putting together a hackathon is not an easy feat, like really not easy. While ACM UTSA's tech team can't help you with delayed packages, food orders being botched, or any of the other issues that can arise at the worst times, we can help you with the tech to make your hackathon go smoothly. HackKit is battle-tested and feature-packed to help your hackathon be the best it can be. From a seamless registration, to an easy-to-use admin panel, to event passes and more, HackKit has you covered.

+

Putting together a hackathon is not an easy feat, like really not easy. While we can't help you with delayed packages, food orders being botched, or any of the other issues that can arise at the worst times, we can help you with the tech to make your hackathon go smoothly. HackKit is battle-tested and feature-packed to help your hackathon be the best it can be. From a seamless registration, to an easy-to-use admin panel, to event passes and more, HackKit has you covered.

Want to get started with using HackKit? Check out our OSS Documents that will be updated as we add new features and fix bugs!

Have any questions, feedback, or need help hosting? Join our Discord or Contact our projects officer.

-**The offical website for RowdyHacks 2024!** - ## Technologies Used - TypeScript @@ -21,12 +19,10 @@ ## Join The Team -Want to help make RowdyHacks the best Hackathon around? Please reach out to us at `team@rowdyhacks.org`! +Want to help us build HackKit? Please reach out to us at `tech@acmutsa.org` or join us on [Discord](https://go.acmutsa.org/projectsdiscord)! -## RH 2024 Website Contributors +## HackKit Contributors - - + + - -[📣❤️ Other Contributions ❤️📣](https://github.com/UTSA-ACM/RowdyHacks24/blob/dev/contributions.md) diff --git a/apps/bot/bot.ts b/apps/bot/bot.ts index 92140193..8d582822 100644 --- a/apps/bot/bot.ts +++ b/apps/bot/bot.ts @@ -151,7 +151,9 @@ app.get("/postMsgToServer", (h) => { text: "Questions or issues? Contact an organizer :)", iconURL: "https://static.acmutsa.org/Info_Simple.svg.png", }); - + console.log( + `server type is: ${serverType}\nDev channel id is: ${process.env.DISCORD_DEV_VERIFY_CHANNEL_ID}\nProd channel id is: ${process.env.DISCORD_PROD_VERIFY_CHANNEL_ID}`, + ); const channel = client.channels.cache.get( serverType === "dev" ? (process.env.DISCORD_DEV_VERIFY_CHANNEL_ID as string) @@ -224,6 +226,9 @@ app.post("/api/checkDiscordVerification", async (h) => { (role) => role.name === userGroupRoleName, ); + // console.log('role is: ', role); + // console.log('userGroupRole is: ', userGroupRole); + if (!role || !userGroupRole) { console.log( "failed cause could not find a role, was looking for group " + @@ -234,16 +239,25 @@ app.post("/api/checkDiscordVerification", async (h) => { return h.json({ success: false }); } + console.log("attempting to get member..."); + const member = guild.members.cache.get(verification.discordUserID); if (!member) { console.log("failed cause could not find member"); return h.json({ success: false }); } - + console.log("got member"); + // Holy waterfalling request + console.log("adding role"); await member.roles.add(role); + console.log("added role"); + console.log("adding group role"); await member.roles.add(userGroupRole); + console.log("added group role"); + console.log("setting nickname"); await member.setNickname(user.firstName + " " + user.lastName); + console.log("set nickname"); return h.json({ success: true }); }); diff --git a/apps/web/package.json b/apps/web/package.json index a509f1cf..eb19a435 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -64,7 +64,7 @@ "lucide-react": "^0.411.0", "nanoid": "^5.0.7", "next": "14.2.5", - "next-safe-action": "^5.2.3", + "next-safe-action": "^7.9.3", "no-profanity": "^1.5.1", "pg": "^8.12.0", "postcss": "8.4.39", diff --git a/apps/web/public/img/SWIVEL Knockout-b.svg b/apps/web/public/img/SWIVEL Knockout-b.svg new file mode 100644 index 00000000..9fa59080 --- /dev/null +++ b/apps/web/public/img/SWIVEL Knockout-b.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/img/SWIVEL Knockout-p.svg b/apps/web/public/img/SWIVEL Knockout-p.svg new file mode 100644 index 00000000..13a4492b --- /dev/null +++ b/apps/web/public/img/SWIVEL Knockout-p.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/img/SWIVEL RGB.svg b/apps/web/public/img/SWIVEL RGB.svg new file mode 100644 index 00000000..2b0dfca3 --- /dev/null +++ b/apps/web/public/img/SWIVEL RGB.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/img/sponsors/CYMANII.svg b/apps/web/public/img/sponsors/CYMANII.svg new file mode 100644 index 00000000..1f24551a --- /dev/null +++ b/apps/web/public/img/sponsors/CYMANII.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/sponsors/DELL.svg b/apps/web/public/img/sponsors/DELL.svg new file mode 100644 index 00000000..8f88bb30 --- /dev/null +++ b/apps/web/public/img/sponsors/DELL.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/img/sponsors/GROQ.png b/apps/web/public/img/sponsors/GROQ.png new file mode 100644 index 00000000..506227ed Binary files /dev/null and b/apps/web/public/img/sponsors/GROQ.png differ diff --git a/apps/web/public/img/sponsors/HEB.svg b/apps/web/public/img/sponsors/HEB.svg new file mode 100644 index 00000000..0a43d02b --- /dev/null +++ b/apps/web/public/img/sponsors/HEB.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/sponsors/KLESSE.svg b/apps/web/public/img/sponsors/KLESSE.svg new file mode 100644 index 00000000..10b5ec9d --- /dev/null +++ b/apps/web/public/img/sponsors/KLESSE.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/img/sponsors/MATRIX.png b/apps/web/public/img/sponsors/MATRIX.png new file mode 100644 index 00000000..ebd8fb5e Binary files /dev/null and b/apps/web/public/img/sponsors/MATRIX.png differ diff --git a/apps/web/public/img/sponsors/SCHROEDER.png b/apps/web/public/img/sponsors/SCHROEDER.png new file mode 100644 index 00000000..8b308207 Binary files /dev/null and b/apps/web/public/img/sponsors/SCHROEDER.png differ diff --git a/apps/web/public/img/sponsors/SWIVEL Knockout-b.svg b/apps/web/public/img/sponsors/SWIVEL Knockout-b.svg new file mode 100644 index 00000000..9fa59080 --- /dev/null +++ b/apps/web/public/img/sponsors/SWIVEL Knockout-b.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/web/public/img/sponsors/SWIVEL.svg b/apps/web/public/img/sponsors/SWIVEL.svg new file mode 100644 index 00000000..f003f366 --- /dev/null +++ b/apps/web/public/img/sponsors/SWIVEL.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/sponsors/UTSA_CS.svg b/apps/web/public/img/sponsors/UTSA_CS.svg new file mode 100644 index 00000000..9733fe6f --- /dev/null +++ b/apps/web/public/img/sponsors/UTSA_CS.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/sponsors/UTSA_DATA_SCIENCE.svg b/apps/web/public/img/sponsors/UTSA_DATA_SCIENCE.svg new file mode 100644 index 00000000..531e83f5 --- /dev/null +++ b/apps/web/public/img/sponsors/UTSA_DATA_SCIENCE.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/team/personality/alejandro-mugica.jpg b/apps/web/public/img/team/personality/alejandro-mugica.jpg new file mode 100644 index 00000000..ce4de724 Binary files /dev/null and b/apps/web/public/img/team/personality/alejandro-mugica.jpg differ diff --git a/apps/web/public/img/team/personality/alesia-tonegari.jpg b/apps/web/public/img/team/personality/alesia-tonegari.jpg new file mode 100644 index 00000000..3603336f Binary files /dev/null and b/apps/web/public/img/team/personality/alesia-tonegari.jpg differ diff --git a/apps/web/public/img/team/personality/alessandro-espinosa.jpg b/apps/web/public/img/team/personality/alessandro-espinosa.jpg new file mode 100644 index 00000000..ae5c6003 Binary files /dev/null and b/apps/web/public/img/team/personality/alessandro-espinosa.jpg differ diff --git a/apps/web/public/img/team/personality/atticus-ramirez.jpg b/apps/web/public/img/team/personality/atticus-ramirez.jpg new file mode 100644 index 00000000..0c5636a0 Binary files /dev/null and b/apps/web/public/img/team/personality/atticus-ramirez.jpg differ diff --git a/apps/web/public/img/team/personality/carter-lavigne.jpeg b/apps/web/public/img/team/personality/carter-lavigne.jpeg new file mode 100644 index 00000000..6a1e959a Binary files /dev/null and b/apps/web/public/img/team/personality/carter-lavigne.jpeg differ diff --git a/apps/web/public/img/team/personality/christian-walker.jpg b/apps/web/public/img/team/personality/christian-walker.jpg new file mode 100644 index 00000000..2a74fcf8 Binary files /dev/null and b/apps/web/public/img/team/personality/christian-walker.jpg differ diff --git a/apps/web/public/img/team/personality/daniel-ruiz.jpg b/apps/web/public/img/team/personality/daniel-ruiz.jpg new file mode 100644 index 00000000..a237d560 Binary files /dev/null and b/apps/web/public/img/team/personality/daniel-ruiz.jpg differ diff --git a/apps/web/public/img/team/personality/eddie-ortiz.jpeg b/apps/web/public/img/team/personality/eddie-ortiz.jpeg new file mode 100644 index 00000000..a8696c32 Binary files /dev/null and b/apps/web/public/img/team/personality/eddie-ortiz.jpeg differ diff --git a/apps/web/public/img/team/personality/inor-wang.jpg b/apps/web/public/img/team/personality/inor-wang.jpg new file mode 100644 index 00000000..2ff4e8fd Binary files /dev/null and b/apps/web/public/img/team/personality/inor-wang.jpg differ diff --git a/apps/web/public/img/team/personality/jacob-ellerbrock.jpg b/apps/web/public/img/team/personality/jacob-ellerbrock.jpg new file mode 100644 index 00000000..2b18c0fd Binary files /dev/null and b/apps/web/public/img/team/personality/jacob-ellerbrock.jpg differ diff --git a/apps/web/public/img/team/personality/jibril-pascua.jpg b/apps/web/public/img/team/personality/jibril-pascua.jpg new file mode 100644 index 00000000..e215f399 Binary files /dev/null and b/apps/web/public/img/team/personality/jibril-pascua.jpg differ diff --git a/apps/web/public/img/team/personality/kailey-perrino.jpg b/apps/web/public/img/team/personality/kailey-perrino.jpg new file mode 100644 index 00000000..0632cce9 Binary files /dev/null and b/apps/web/public/img/team/personality/kailey-perrino.jpg differ diff --git a/apps/web/public/img/team/personality/kathy-nguyen.jpg b/apps/web/public/img/team/personality/kathy-nguyen.jpg new file mode 100644 index 00000000..f43433ad Binary files /dev/null and b/apps/web/public/img/team/personality/kathy-nguyen.jpg differ diff --git a/apps/web/public/img/team/personality/kelinda-shen.jpg b/apps/web/public/img/team/personality/kelinda-shen.jpg new file mode 100644 index 00000000..3c3eb675 Binary files /dev/null and b/apps/web/public/img/team/personality/kelinda-shen.jpg differ diff --git a/apps/web/public/img/team/personality/liam-murray.jpg b/apps/web/public/img/team/personality/liam-murray.jpg new file mode 100644 index 00000000..2baa4ed5 Binary files /dev/null and b/apps/web/public/img/team/personality/liam-murray.jpg differ diff --git a/apps/web/public/img/team/personality/mei-sullum.jpeg b/apps/web/public/img/team/personality/mei-sullum.jpeg new file mode 100644 index 00000000..a5989dae Binary files /dev/null and b/apps/web/public/img/team/personality/mei-sullum.jpeg differ diff --git a/apps/web/public/img/team/personality/miguel-oseguera.JPG b/apps/web/public/img/team/personality/miguel-oseguera.JPG new file mode 100644 index 00000000..7f3995c0 Binary files /dev/null and b/apps/web/public/img/team/personality/miguel-oseguera.JPG differ diff --git a/apps/web/public/img/team/personality/natasha-blussick.jpeg b/apps/web/public/img/team/personality/natasha-blussick.jpeg new file mode 100644 index 00000000..273c2e44 Binary files /dev/null and b/apps/web/public/img/team/personality/natasha-blussick.jpeg differ diff --git a/apps/web/public/img/team/personality/natasha-blussick.jpg b/apps/web/public/img/team/personality/natasha-blussick.jpg new file mode 100644 index 00000000..a28e854a Binary files /dev/null and b/apps/web/public/img/team/personality/natasha-blussick.jpg differ diff --git a/apps/web/public/img/team/personality/nathan-zuniga.jpg b/apps/web/public/img/team/personality/nathan-zuniga.jpg new file mode 100644 index 00000000..da5e6479 Binary files /dev/null and b/apps/web/public/img/team/personality/nathan-zuniga.jpg differ diff --git a/apps/web/public/img/team/personality/paolo-lay.jpg b/apps/web/public/img/team/personality/paolo-lay.jpg new file mode 100644 index 00000000..1b2f2b2e Binary files /dev/null and b/apps/web/public/img/team/personality/paolo-lay.jpg differ diff --git a/apps/web/public/img/team/personality/rahul-paul.jpg b/apps/web/public/img/team/personality/rahul-paul.jpg new file mode 100644 index 00000000..0d035aeb Binary files /dev/null and b/apps/web/public/img/team/personality/rahul-paul.jpg differ diff --git a/apps/web/public/img/team/personality/reese-sylvester.JPG b/apps/web/public/img/team/personality/reese-sylvester.JPG new file mode 100644 index 00000000..7838d964 Binary files /dev/null and b/apps/web/public/img/team/personality/reese-sylvester.JPG differ diff --git a/apps/web/public/img/team/personality/sean-jauregui.jpg b/apps/web/public/img/team/personality/sean-jauregui.jpg new file mode 100644 index 00000000..300a3163 Binary files /dev/null and b/apps/web/public/img/team/personality/sean-jauregui.jpg differ diff --git a/apps/web/public/img/team/personality/sean-otoole.jpg b/apps/web/public/img/team/personality/sean-otoole.jpg new file mode 100644 index 00000000..e14bdf33 Binary files /dev/null and b/apps/web/public/img/team/personality/sean-otoole.jpg differ diff --git a/apps/web/public/img/team/personality/tosin-adefashe.jpg b/apps/web/public/img/team/personality/tosin-adefashe.jpg new file mode 100644 index 00000000..8742165f Binary files /dev/null and b/apps/web/public/img/team/personality/tosin-adefashe.jpg differ diff --git a/apps/web/public/img/team/personality/trae-ramirez.jpg b/apps/web/public/img/team/personality/trae-ramirez.jpg new file mode 100644 index 00000000..11c2241e Binary files /dev/null and b/apps/web/public/img/team/personality/trae-ramirez.jpg differ diff --git a/apps/web/public/img/team/personality/vivian-tran.jpg b/apps/web/public/img/team/personality/vivian-tran.jpg new file mode 100644 index 00000000..aff09f6d Binary files /dev/null and b/apps/web/public/img/team/personality/vivian-tran.jpg differ diff --git a/apps/web/public/img/team/professional/alejandro-mugica.jpeg b/apps/web/public/img/team/professional/alejandro-mugica.jpeg new file mode 100644 index 00000000..58d84008 Binary files /dev/null and b/apps/web/public/img/team/professional/alejandro-mugica.jpeg differ diff --git a/apps/web/public/img/team/professional/alesia-tonegari.jpg b/apps/web/public/img/team/professional/alesia-tonegari.jpg new file mode 100644 index 00000000..93b3e527 Binary files /dev/null and b/apps/web/public/img/team/professional/alesia-tonegari.jpg differ diff --git a/apps/web/public/img/team/professional/alessandro-espinosa.jpg b/apps/web/public/img/team/professional/alessandro-espinosa.jpg new file mode 100644 index 00000000..a442fe60 Binary files /dev/null and b/apps/web/public/img/team/professional/alessandro-espinosa.jpg differ diff --git a/apps/web/public/img/team/professional/atticus-ramirez.jpg b/apps/web/public/img/team/professional/atticus-ramirez.jpg new file mode 100644 index 00000000..5a0d5134 Binary files /dev/null and b/apps/web/public/img/team/professional/atticus-ramirez.jpg differ diff --git a/apps/web/public/img/team/professional/carter-lavigne.jpeg b/apps/web/public/img/team/professional/carter-lavigne.jpeg new file mode 100644 index 00000000..4d78e750 Binary files /dev/null and b/apps/web/public/img/team/professional/carter-lavigne.jpeg differ diff --git a/apps/web/public/img/team/professional/christian-walker.jpg b/apps/web/public/img/team/professional/christian-walker.jpg new file mode 100644 index 00000000..27215675 Binary files /dev/null and b/apps/web/public/img/team/professional/christian-walker.jpg differ diff --git a/apps/web/public/img/team/professional/daniel-ruiz.jpg b/apps/web/public/img/team/professional/daniel-ruiz.jpg new file mode 100644 index 00000000..9a11eb7b Binary files /dev/null and b/apps/web/public/img/team/professional/daniel-ruiz.jpg differ diff --git a/apps/web/public/img/team/professional/eddie-ortiz.jpeg b/apps/web/public/img/team/professional/eddie-ortiz.jpeg new file mode 100644 index 00000000..8b563a31 Binary files /dev/null and b/apps/web/public/img/team/professional/eddie-ortiz.jpeg differ diff --git a/apps/web/public/img/team/professional/inor-wang.jpg b/apps/web/public/img/team/professional/inor-wang.jpg new file mode 100644 index 00000000..df5a657b Binary files /dev/null and b/apps/web/public/img/team/professional/inor-wang.jpg differ diff --git a/apps/web/public/img/team/professional/jacob-ellerbrock.jpg b/apps/web/public/img/team/professional/jacob-ellerbrock.jpg new file mode 100644 index 00000000..b31ecfae Binary files /dev/null and b/apps/web/public/img/team/professional/jacob-ellerbrock.jpg differ diff --git a/apps/web/public/img/team/professional/jibril-pascua.jpg b/apps/web/public/img/team/professional/jibril-pascua.jpg new file mode 100644 index 00000000..81f380e8 Binary files /dev/null and b/apps/web/public/img/team/professional/jibril-pascua.jpg differ diff --git a/apps/web/public/img/team/professional/kailey-perrino.jpg b/apps/web/public/img/team/professional/kailey-perrino.jpg new file mode 100644 index 00000000..592643f5 Binary files /dev/null and b/apps/web/public/img/team/professional/kailey-perrino.jpg differ diff --git a/apps/web/public/img/team/professional/kathy-nguyen.jpg b/apps/web/public/img/team/professional/kathy-nguyen.jpg new file mode 100644 index 00000000..b47352c7 Binary files /dev/null and b/apps/web/public/img/team/professional/kathy-nguyen.jpg differ diff --git a/apps/web/public/img/team/professional/kelinda-shen.jpg b/apps/web/public/img/team/professional/kelinda-shen.jpg new file mode 100644 index 00000000..d08f755e Binary files /dev/null and b/apps/web/public/img/team/professional/kelinda-shen.jpg differ diff --git a/apps/web/public/img/team/professional/liam-murray.jpeg b/apps/web/public/img/team/professional/liam-murray.jpeg new file mode 100644 index 00000000..0d1802bc Binary files /dev/null and b/apps/web/public/img/team/professional/liam-murray.jpeg differ diff --git a/apps/web/public/img/team/professional/mei-sullum.jpeg b/apps/web/public/img/team/professional/mei-sullum.jpeg new file mode 100644 index 00000000..b26fb791 Binary files /dev/null and b/apps/web/public/img/team/professional/mei-sullum.jpeg differ diff --git a/apps/web/public/img/team/professional/miguel-oseguera.JPG b/apps/web/public/img/team/professional/miguel-oseguera.JPG new file mode 100644 index 00000000..e161319a Binary files /dev/null and b/apps/web/public/img/team/professional/miguel-oseguera.JPG differ diff --git a/apps/web/public/img/team/professional/natasha-blussick.jpg b/apps/web/public/img/team/professional/natasha-blussick.jpg new file mode 100644 index 00000000..4a28f67e Binary files /dev/null and b/apps/web/public/img/team/professional/natasha-blussick.jpg differ diff --git a/apps/web/public/img/team/professional/nathan-zuniga.jpg b/apps/web/public/img/team/professional/nathan-zuniga.jpg new file mode 100644 index 00000000..805229df Binary files /dev/null and b/apps/web/public/img/team/professional/nathan-zuniga.jpg differ diff --git a/apps/web/public/img/team/professional/paolo-lay.jpg b/apps/web/public/img/team/professional/paolo-lay.jpg new file mode 100644 index 00000000..aed34a35 Binary files /dev/null and b/apps/web/public/img/team/professional/paolo-lay.jpg differ diff --git a/apps/web/public/img/team/professional/rahul-paul.jpg b/apps/web/public/img/team/professional/rahul-paul.jpg new file mode 100644 index 00000000..91297ca0 Binary files /dev/null and b/apps/web/public/img/team/professional/rahul-paul.jpg differ diff --git a/apps/web/public/img/team/professional/reese-sylvester.JPG b/apps/web/public/img/team/professional/reese-sylvester.JPG new file mode 100644 index 00000000..48a78aa9 Binary files /dev/null and b/apps/web/public/img/team/professional/reese-sylvester.JPG differ diff --git a/apps/web/public/img/team/professional/sean-jauregui.jpeg b/apps/web/public/img/team/professional/sean-jauregui.jpeg new file mode 100644 index 00000000..77b728b4 Binary files /dev/null and b/apps/web/public/img/team/professional/sean-jauregui.jpeg differ diff --git a/apps/web/public/img/team/professional/sean-otoole.JPG b/apps/web/public/img/team/professional/sean-otoole.JPG new file mode 100644 index 00000000..6f6222bb Binary files /dev/null and b/apps/web/public/img/team/professional/sean-otoole.JPG differ diff --git a/apps/web/public/img/team/professional/tosin-adefashe.jpg b/apps/web/public/img/team/professional/tosin-adefashe.jpg new file mode 100644 index 00000000..9fec77d4 Binary files /dev/null and b/apps/web/public/img/team/professional/tosin-adefashe.jpg differ diff --git a/apps/web/public/img/team/professional/trae-ramirez.jpg b/apps/web/public/img/team/professional/trae-ramirez.jpg new file mode 100644 index 00000000..2cc1210c Binary files /dev/null and b/apps/web/public/img/team/professional/trae-ramirez.jpg differ diff --git a/apps/web/public/img/team/professional/vivian-tran.png b/apps/web/public/img/team/professional/vivian-tran.png new file mode 100644 index 00000000..7461d089 Binary files /dev/null and b/apps/web/public/img/team/professional/vivian-tran.png differ diff --git a/apps/web/public/team.json b/apps/web/public/team.json new file mode 100644 index 00000000..125e113d --- /dev/null +++ b/apps/web/public/team.json @@ -0,0 +1,544 @@ +{ + "team": [ + { + "name": "Nathan Zuniga", + "nameSize": 3, + "team": "Director", + "professionalPicture": { + "link": "/img/team/professional/nathan-zuniga.jpg", + "zoom": 2.5, + "x": 15, + "y": 80 + }, + "personalityPicture": { + "link": "/img/team/personality/nathan-zuniga.jpg", + "zoom": 1.7, + "x": 0, + "y": 100 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Liam Murray", + "nameSize": 3, + "team": "Co-Director", + "professionalPicture": { + "link": "/img/team/professional/liam-murray.jpeg", + "zoom": 1.7, + "x": -20, + "y": 0 + }, + "personalityPicture": { + "link": "/img/team/personality/liam-murray.jpg", + "zoom": 2.4, + "x": 96, + "y": 80 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Mei Sullum", + "nameSize": 3, + "team": "Logistics", + "professionalPicture": { + "link": "/img/team/professional/mei-sullum.jpeg", + "zoom": 1.3, + "x": 0, + "y": 15 + }, + "personalityPicture": { + "link": "/img/team/personality/mei-sullum.jpeg", + "zoom": 1.1, + "x": 0, + "y": -15 + }, + "linkedin": "https://www.linkedin.com/in/meira-sullum-55198521b/", + "github": "", + "personal": "" + }, + { + "name": "Natasha Blussick", + "nameSize": 2, + "team": "Logistics / PR", + "professionalPicture": { + "link": "/img/team/professional/natasha-blussick.jpg", + "zoom": 1.4, + "x": 0, + "y": -10 + }, + "personalityPicture": { + "link": "/img/team/personality/natasha-blussick.jpeg", + "zoom": 1.2, + "x": 0, + "y": 0 + }, + "linkedin": "https://www.linkedin.com/in/natasha-blussick/", + "github": "", + "personal": "" + }, + { + "name": "Alejandro Mugica", + "nameSize": 2, + "team": "Logistics", + "professionalPicture": { + "link": "/img/team/professional/alejandro-mugica.jpeg", + "zoom": 1.5, + "x": -28, + "y": 0 + }, + "personalityPicture": { + "link": "/img/team/personality/alejandro-mugica.jpg", + "zoom": 1.2, + "x": 0, + "y": 0 + }, + "linkedin": "www.linkedin.com/in/alejandromugica", + "github": "", + "personal": "" + }, + { + "name": "Paolo Lay", + "nameSize": 3, + "team": "Logistics", + "professionalPicture": { + "link": "/img/team/professional/paolo-lay.jpg", + "zoom": 1.3, + "x": 30, + "y": 0 + }, + "personalityPicture": { + "link": "/img/team/personality/paolo-lay.jpg", + "zoom": 2.3, + "x": 5, + "y": 86 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Trae Ramirez", + "nameSize": 3, + "team": "Hacker Experience", + "professionalPicture": { + "link": "/img/team/professional/trae-ramirez.jpg", + "zoom": 2.3, + "x": 90, + "y": 80 + }, + "personalityPicture": { + "link": "/img/team/personality/trae-ramirez.jpg", + "zoom": 2.3, + "x": 90, + "y": 80 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Vivian Tran", + "nameSize": 3, + "team": "Hacker Experience", + "professionalPicture": { + "link": "/img/team/professional/vivian-tran.png", + "zoom": 2, + "x": 0, + "y": 8 + }, + "personalityPicture": { + "link": "/img/team/personality/vivian-tran.jpg", + "zoom": 2.3, + "x": -80, + "y": 90 + }, + "linkedin": "https://www.linkedin.com/in/vivtratx/", + "github": "", + "personal": "" + }, + { + "name": "Christian Walker", + "nameSize": 2, + "team": "Tech", + "professionalPicture": { + "link": "/img/team/professional/christian-walker.jpg", + "zoom": 2.6, + "x": 10, + "y": 70 + }, + "personalityPicture": { + "link": "/img/team/personality/christian-walker.jpg", + "zoom": 1.1, + "x": 0, + "y": 0 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Jacob Ellerbrock", + "nameSize": 2, + "team": "Tech / Logistics", + "professionalPicture": { + "link": "/img/team/professional/jacob-ellerbrock.jpg", + "zoom": 2, + "x": -10, + "y": 80 + }, + "personalityPicture": { + "link": "/img/team/personality/jacob-ellerbrock.jpg", + "zoom": 2.3, + "x": 70, + "y": 89 + }, + "linkedin": "https://www.linkedin.com/in/jacobellerbrock", + "github": "https://github.com/jacobellerbrock", + "personal": "" + }, + { + "name": "Carter LaVigne", + "nameSize": 3, + "team": "Tech", + "professionalPicture": { + "link": "/img/team/professional/carter-lavigne.jpeg", + "zoom": 1.8, + "x": 55, + "y": 85 + }, + "personalityPicture": { + "link": "/img/team/personality/carter-lavigne.jpeg", + "zoom": 1.3, + "x": 30, + "y": 0 + }, + "linkedin": "https://www.linkedin.com/in/carter-m-lavigne", + "github": "https://github.com/spiderCSman", + "personal": "" + }, + { + "name": "Alessandro Espinosa", + "nameSize": 1.8, + "team": "Media", + "professionalPicture": { + "link": "/img/team/professional/alessandro-espinosa.jpg", + "zoom": 1.2, + "x": 0, + "y": 15 + }, + "personalityPicture": { + "link": "/img/team/personality/alessandro-espinosa.jpg", + "zoom": 1, + "x": 0, + "y": 0 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Daniel Ruiz", + "nameSize": 3, + "team": "Media", + "professionalPicture": { + "link": "/img/team/professional/daniel-ruiz.jpg", + "zoom": 1.4, + "x": -40, + "y": 30 + }, + "personalityPicture": { + "link": "/img/team/personality/daniel-ruiz.jpg", + "zoom": 1, + "x": 0, + "y": 0 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Sean O'Toole", + "nameSize": 3, + "team": "Media", + "professionalPicture": { + "link": "/img/team/professional/sean-otoole.JPG", + "zoom": 1.4, + "x": 20, + "y": -50 + }, + "personalityPicture": { + "link": "/img/team/personality/sean-otoole.jpg", + "zoom": 1.2, + "x": 0, + "y": 10 + }, + "linkedin": "https://www.linkedin.com/in/seanotoole04/", + "github": "https://github.com/SeanAidanOToole", + "personal": "" + }, + { + "name": "Inor Wang", + "nameSize": 3, + "team": "Media", + "professionalPicture": { + "link": "/img/team/professional/inor-wang.jpg", + "zoom": 1.4, + "x": 41, + "y": 40 + }, + "personalityPicture": { + "link": "/img/team/personality/inor-wang.jpg", + "zoom": 1.2, + "x": 0, + "y": 0 + }, + "linkedin": "https://www.linkedin.com/in/inorwang", + "github": "https://github.com/inor-w", + "personal": "" + }, + { + "name": "Sean Jauregui", + "nameSize": 3, + "team": "Media", + "professionalPicture": { + "link": "/img/team/professional/sean-jauregui.jpeg", + "zoom": 1.2, + "x": 0, + "y": 10 + }, + "personalityPicture": { + "link": "/img/team/personality/sean-jauregui.jpg", + "zoom": 2.35, + "x": -33, + "y": 90 + }, + "linkedin": "https://www.linkedin.com/in/seanjauregui", + "github": "", + "personal": "https://www.instagram.com/scottish_artist3379/" + }, + { + "name": "Jibril Pascua", + "nameSize": 3, + "team": "Media", + "professionalPicture": { + "link": "/img/team/professional/jibril-pascua.jpg", + "zoom": 1.3, + "x": 0, + "y": 40 + }, + "personalityPicture": { + "link": "/img/team/personality/jibril-pascua.jpg", + "zoom": 1.3, + "x": 0, + "y": 30 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Atticus Ramirez", + "nameSize": 3, + "team": "Media", + "professionalPicture": { + "link": "/img/team/professional/atticus-ramirez.jpg", + "zoom": 1.5, + "x": 0, + "y": 35 + }, + "personalityPicture": { + "link": "/img/team/personality/atticus-ramirez.jpg", + "zoom": 1.2, + "x": 0, + "y": 0 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Tosin Adefashe", + "nameSize": 3, + "team": "Media", + "professionalPicture": { + "link": "/img/team/professional/tosin-adefashe.jpg", + "zoom": 1.7, + "x": 10, + "y": 0 + }, + "personalityPicture": { + "link": "/img/team/personality/tosin-adefashe.jpg", + "zoom": 1.5, + "x": 10, + "y": 40 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Alesia Tonegari", + "nameSize": 3, + "team": "Media", + "professionalPicture": { + "link": "/img/team/professional/alesia-tonegari.jpg", + "zoom": 1.4, + "x": -40, + "y": 30 + }, + "personalityPicture": { + "link": "/img/team/personality/alesia-tonegari.jpg", + "zoom": 1.2, + "x": 0, + "y": 10 + }, + "linkedin": "https://www.linkedin.com/in/alesia-tonegari-436aa72b0?utm_source=share&utm_campaign=share_via&utm_content=profile&utm_medium=ios_app", + "github": "", + "personal": "" + }, + { + "name": "Kelinda Shen", + "nameSize": 3, + "team": "Public Relations", + "professionalPicture": { + "link": "/img/team/professional/kelinda-shen.jpg", + "zoom": 2.3, + "x": -70, + "y": 89 + }, + "personalityPicture": { + "link": "/img/team/personality/kelinda-shen.jpg", + "zoom": 2.3, + "x": -70, + "y": 89 + }, + "linkedin": "www.linkedin.com/in/reese-sylvester-234490242", + "github": "", + "personal": "https://www.instagram.com/sawinez/" + }, + { + "name": "Kailey Perrino", + "nameSize": 3, + "team": "Public Relations", + "professionalPicture": { + "link": "/img/team/professional/kailey-perrino.jpg", + "zoom": 1.1, + "x": 0, + "y": 0 + }, + "personalityPicture": { + "link": "/img/team/personality/kailey-perrino.jpg", + "zoom": 2.3, + "x": 100, + "y": 90 + }, + "linkedin": "https://www.linkedin.com/in/kailey-perrino/", + "github": "", + "personal": "" + }, + { + "name": "Reese Sylvester", + "nameSize": 3, + "team": "Public Relations", + "professionalPicture": { + "link": "/img/team/professional/reese-sylvester.JPG", + "zoom": 2, + "x": 0, + "y": 20 + }, + "personalityPicture": { + "link": "/img/team/personality/reese-sylvester.JPG", + "zoom": 2.55, + "x": 0, + "y": 70 + }, + "linkedin": "https://www.linkedin.com/in/reese-sylvester-234490242", + "github": "", + "personal": "https://www.instagram.com/sawinez/" + }, + { + "name": "Miguel Oseguera", + "nameSize": 2, + "team": "Public Relations", + "professionalPicture": { + "link": "/img/team/professional/miguel-oseguera.JPG", + "zoom": 1.2, + "x": 0, + "y": -25 + }, + "personalityPicture": { + "link": "/img/team/personality/miguel-oseguera.JPG", + "zoom": 1.2, + "x": 0, + "y": 0 + }, + "linkedin": "https://www.linkedin.com/in/miguel-oseguera-0b5306281", + "github": "", + "personal": "" + }, + { + "name": "Kathy Nguyen", + "nameSize": 2, + "team": "Design", + "professionalPicture": { + "link": "/img/team/professional/kathy-nguyen.jpg", + "zoom": 1.5, + "x": 0, + "y": 13 + }, + "personalityPicture": { + "link": "/img/team/personality/kathy-nguyen.jpg", + "zoom": 2.4, + "x": 55, + "y": 82 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Rahul Paul", + "nameSize": 3, + "team": "Design", + "professionalPicture": { + "link": "/img/team/professional/rahul-paul.jpg", + "zoom": 1.3, + "x": 0, + "y": 10 + }, + "personalityPicture": { + "link": "/img/team/personality/rahul-paul.jpg", + "zoom": 1.5, + "x": 10, + "y": 30 + }, + "linkedin": "", + "github": "", + "personal": "" + }, + { + "name": "Eddie Ortiz", + "nameSize": 3, + "team": "Outreach", + "professionalPicture": { + "link": "/img/team/professional/eddie-ortiz.jpeg", + "zoom": 1.3, + "x": 0, + "y": 0 + }, + "personalityPicture": { + "link": "/img/team/personality/eddie-ortiz.jpeg", + "zoom": 1, + "x": 0, + "y": 0 + }, + "linkedin": "http://linkedin.com/in/joseortiz-jeo", + "github": "https://github.com/Buffet936", + "personal": "" + } + ] +} \ No newline at end of file diff --git a/apps/web/src/actions/admin/modify-nav-item.ts b/apps/web/src/actions/admin/modify-nav-item.ts index b52e253b..5dc3a48e 100644 --- a/apps/web/src/actions/admin/modify-nav-item.ts +++ b/apps/web/src/actions/admin/modify-nav-item.ts @@ -13,9 +13,9 @@ const metadataSchema = z.object({ // Maybe a better way to do this for revalidation? Who knows. const navAdminPage = "/admin/toggles/landing"; -export const setItem = adminAction( - metadataSchema, - async ({ name, url }, { user, userId }) => { +export const setItem = adminAction + .schema(metadataSchema) + .action(async ({ parsedInput: { name, url }, ctx: { user, userId } }) => { await kv.sadd("config:navitemslist", encodeURIComponent(name)); await kv.hset(`config:navitems:${encodeURIComponent(name)}`, { url, @@ -24,12 +24,11 @@ export const setItem = adminAction( }); revalidatePath(navAdminPage); return { success: true }; - }, -); + }); -export const removeItem = adminAction( - z.string(), - async (name, { user, userId }) => { +export const removeItem = adminAction + .schema(z.string()) + .action(async ({ parsedInput: name, ctx: { user, userId } }) => { const pipe = kv.pipeline(); pipe.srem("config:navitemslist", encodeURIComponent(name)); pipe.del(`config:navitems:${encodeURIComponent(name)}`); @@ -37,16 +36,19 @@ export const removeItem = adminAction( // await new Promise((resolve) => setTimeout(resolve, 1500)); revalidatePath(navAdminPage); return { success: true }; - }, -); + }); -export const toggleItem = adminAction( - z.object({ name: z.string(), statusToSet: z.boolean() }), - async ({ name, statusToSet }, { user, userId }) => { - await kv.hset(`config:navitems:${encodeURIComponent(name)}`, { - enabled: statusToSet, - }); - revalidatePath(navAdminPage); - return { success: true, itemStatus: statusToSet }; - }, -); +export const toggleItem = adminAction + .schema(z.object({ name: z.string(), statusToSet: z.boolean() })) + .action( + async ({ + parsedInput: { name, statusToSet }, + ctx: { user, userId }, + }) => { + await kv.hset(`config:navitems:${encodeURIComponent(name)}`, { + enabled: statusToSet, + }); + revalidatePath(navAdminPage); + return { success: true, itemStatus: statusToSet }; + }, + ); diff --git a/apps/web/src/actions/admin/registration-actions.ts b/apps/web/src/actions/admin/registration-actions.ts index 9388bbba..f41c10d1 100644 --- a/apps/web/src/actions/admin/registration-actions.ts +++ b/apps/web/src/actions/admin/registration-actions.ts @@ -9,38 +9,34 @@ const defaultRegistrationToggleSchema = z.object({ enabled: z.boolean(), }); -export const toggleRegistrationEnabled = adminAction( - defaultRegistrationToggleSchema, - async ({ enabled }, { user, userId }) => { +export const toggleRegistrationEnabled = adminAction + .schema(defaultRegistrationToggleSchema) + .action(async ({ parsedInput: { enabled }, ctx: { user, userId } }) => { await kv.set("config:registration:registrationEnabled", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; - }, -); + }); -export const toggleRegistrationMessageEnabled = adminAction( - defaultRegistrationToggleSchema, - async ({ enabled }, { user, userId }) => { +export const toggleRegistrationMessageEnabled = adminAction + .schema(defaultRegistrationToggleSchema) + .action(async ({ parsedInput: { enabled }, ctx: { user, userId } }) => { await kv.set("config:registration:registrationMessageEnabled", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; - }, -); + }); -export const toggleSecretRegistrationEnabled = adminAction( - defaultRegistrationToggleSchema, - async ({ enabled }, { user, userId }) => { +export const toggleSecretRegistrationEnabled = adminAction + .schema(defaultRegistrationToggleSchema) + .action(async ({ parsedInput: { enabled }, ctx: { user, userId } }) => { await kv.set("config:registration:secretRegistrationEnabled", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; - }, -); + }); -export const toggleRSVPs = adminAction( - defaultRegistrationToggleSchema, - async ({ enabled }, { user, userId }) => { +export const toggleRSVPs = adminAction + .schema(defaultRegistrationToggleSchema) + .action(async ({ parsedInput: { enabled }, ctx: { user, userId } }) => { await kv.set("config:registration:allowRSVPs", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; - }, -); + }); diff --git a/apps/web/src/actions/admin/scanner-admin-actions.ts b/apps/web/src/actions/admin/scanner-admin-actions.ts index 2faeb2e3..af21fd0e 100644 --- a/apps/web/src/actions/admin/scanner-admin-actions.ts +++ b/apps/web/src/actions/admin/scanner-admin-actions.ts @@ -5,51 +5,84 @@ import { z } from "zod"; import { db, sql } from "db"; import { scans, userCommonData } from "db/schema"; import { eq, and } from "db/drizzle"; -export const createScan = adminAction( - z.object({ - eventID: z.number(), - userID: z.string(), - creationTime: z.date(), - countToSet: z.number(), - alreadyExists: z.boolean(), - }), - async ( - { eventID, userID, creationTime, countToSet, alreadyExists }, - { user, userId }, - ) => { - if (alreadyExists) { - await db - .update(scans) - .set({ count: countToSet, updatedAt: creationTime }) - .where( - and(eq(scans.eventID, eventID), eq(scans.userID, userID)), - ); - } else { - await db.insert(scans).values({ - userID: userID, - updatedAt: creationTime, - count: 1, - eventID: eventID, - }); - } - return { success: true }; - }, -); -export const getScan = adminAction( - z.object({ eventID: z.number(), userID: z.string() }), - async ({ eventID, userID }, { user, userId: adminUserID }) => { - const scan = await db.query.scans.findFirst({ - where: and(eq(scans.eventID, eventID), eq(scans.userID, userID)), - }); - return scan; - }, -); +export const createScan = adminAction + .schema( + z.object({ + eventID: z.number(), + userID: z.string(), + creationTime: z.date(), + countToSet: z.number(), + alreadyExists: z.boolean(), + }), + ) + .action( + async ({ + parsedInput: { + eventID, + userID, + creationTime, + countToSet, + alreadyExists, + }, + ctx: { user, userId }, + }) => { + if (alreadyExists) { + await db + .update(scans) + .set({ count: countToSet, updatedAt: creationTime }) + .where( + and( + eq(scans.eventID, eventID), + eq(scans.userID, userID), + ), + ); + } else { + await db.insert(scans).values({ + userID: userID, + updatedAt: creationTime, + count: 1, + eventID: eventID, + }); + } + return { success: true }; + }, + ); + +export const getScan = adminAction + .schema(z.object({ eventID: z.number(), userID: z.string() })) + .action( + async ({ + parsedInput: { eventID, userID }, + ctx: { user, userId: adminUserID }, + }) => { + const scan = await db.query.scans.findFirst({ + where: and( + eq(scans.eventID, eventID), + eq(scans.userID, userID), + ), + }); + return scan; + }, + ); -export const checkInUser = adminAction(z.string(), async (user) => { - // Set checkinTimestamp - return await db - .update(userCommonData) - .set({ checkinTimestamp: sql`now()` }) - .where(eq(userCommonData.clerkID, user)); +// Schema will be moved over when rewrite of the other scanner happens +const checkInUserSchema = z.object({ + userID: z.string(), + QRTimestamp: z + .number() + .positive() + .refine((timestamp) => { + return Date.now() - timestamp < 5 * 60 * 1000; + }, "QR Code has expired. Please tell user refresh the QR Code"), }); + +export const checkInUserToHackathon = adminAction + .schema(checkInUserSchema) + .action(async ({ parsedInput: { userID } }) => { + // Set checkinTimestamp + await db + .update(userCommonData) + .set({ checkinTimestamp: sql`now()` }) + .where(eq(userCommonData.clerkID, userID)); + }); diff --git a/apps/web/src/actions/admin/user-actions.ts b/apps/web/src/actions/admin/user-actions.ts index 0e650697..41b46656 100644 --- a/apps/web/src/actions/admin/user-actions.ts +++ b/apps/web/src/actions/admin/user-actions.ts @@ -1,6 +1,7 @@ "use server"; import { adminAction } from "@/lib/safe-action"; +import { returnValidationErrors } from "next-safe-action"; import { z } from "zod"; import { perms } from "config"; import { userCommonData } from "db/schema"; @@ -8,39 +9,52 @@ import { db } from "db"; import { eq } from "db/drizzle"; import { revalidatePath } from "next/cache"; -export const updateRole = adminAction( - z.object({ - userIDToUpdate: z.string(), - roleToSet: z.enum(perms), - }), - async ({ userIDToUpdate, roleToSet }, { user, userId }) => { - if ( - user.role !== "super_admin" && - (roleToSet === "super_admin" || roleToSet === "admin") - ) { - throw new Error("You are not allowed to do this"); - } - await db - .update(userCommonData) - .set({ role: roleToSet }) - .where(eq(userCommonData.clerkID, userIDToUpdate)); - revalidatePath(`/admin/users/${userIDToUpdate}`); - return { success: true }; - }, -); +export const updateRole = adminAction + .schema( + z.object({ + userIDToUpdate: z.string(), + roleToSet: z.enum(perms), + }), + ) + .action( + async ({ + parsedInput: { userIDToUpdate, roleToSet }, + ctx: { user, userId }, + }) => { + if ( + user.role !== "super_admin" && + (roleToSet === "super_admin" || roleToSet === "admin") + ) { + returnValidationErrors(z.null(), { + _errors: ["You are not allowed to do this!"], + }); + } + await db + .update(userCommonData) + .set({ role: roleToSet }) + .where(eq(userCommonData.clerkID, userIDToUpdate)); + revalidatePath(`/admin/users/${userIDToUpdate}`); + return { success: true }; + }, + ); -export const setUserApproval = adminAction( - z.object({ - userIDToUpdate: z.string().min(1), - approved: z.boolean(), - }), - - async ({ userIDToUpdate, approved }, { user, userId }) => { - await db - .update(userCommonData) - .set({ isApproved: approved }) - .where(eq(userCommonData.clerkID, userIDToUpdate)); - revalidatePath(`/admin/users/${userIDToUpdate}`); - return { success: true }; - }, -); +export const setUserApproval = adminAction + .schema( + z.object({ + userIDToUpdate: z.string().min(1), + approved: z.boolean(), + }), + ) + .action( + async ({ + parsedInput: { userIDToUpdate, approved }, + ctx: { user, userId }, + }) => { + await db + .update(userCommonData) + .set({ isApproved: approved }) + .where(eq(userCommonData.clerkID, userIDToUpdate)); + revalidatePath(`/admin/users/${userIDToUpdate}`); + return { success: true }; + }, + ); diff --git a/apps/web/src/actions/discord-verify.ts b/apps/web/src/actions/discord-verify.ts index c822885b..ea489515 100644 --- a/apps/web/src/actions/discord-verify.ts +++ b/apps/web/src/actions/discord-verify.ts @@ -7,11 +7,13 @@ import { eq, and } from "db/drizzle"; import { discordVerification } from "db/schema"; import { env } from "@/env"; -export const confirmVerifyDiscord = authenticatedAction( - z.object({ - code: z.string().min(20).max(20), - }), - async ({ code }, { userId }) => { +export const confirmVerifyDiscord = authenticatedAction + .schema( + z.object({ + code: z.string().min(20).max(20), + }), + ) + .action(async ({ parsedInput: { code }, ctx: { userId } }) => { const verification = await db.query.discordVerification.findFirst({ where: and( eq(discordVerification.code, code), @@ -30,24 +32,22 @@ export const confirmVerifyDiscord = authenticatedAction( .update(discordVerification) .set({ status: "accepted", clerkID: userId }) .where(eq(discordVerification.code, code)); - - const res = await fetch( + const url = env.BOT_API_URL + - "/api/checkDiscordVerification?access=" + - env.INTERNAL_AUTH_KEY, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ code }), + "/api/checkDiscordVerification?access=" + + env.INTERNAL_AUTH_KEY; + console.log("url is: ", url); + const res = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", }, - ); + body: JSON.stringify({ code }), + }); let resJson = await res.json(); console.log(resJson); return { success: true, }; - }, -); + }); diff --git a/apps/web/src/actions/rsvp.ts b/apps/web/src/actions/rsvp.ts index a4a30b6d..90724c2a 100644 --- a/apps/web/src/actions/rsvp.ts +++ b/apps/web/src/actions/rsvp.ts @@ -6,12 +6,13 @@ import { db } from "db"; import { eq } from "db/drizzle"; import { userCommonData } from "db/schema"; import { getUser } from "db/functions"; +import { returnValidationErrors } from "next-safe-action"; -export const rsvpMyself = authenticatedAction( - z.any(), - async (_, { userId }) => { +export const rsvpMyself = authenticatedAction.action( + async ({ ctx: { userId } }) => { const user = await getUser(userId); - if (!user) throw new Error("User not found"); + if (!user) + returnValidationErrors(z.null(), { _errors: ["User not found"] }); await db .update(userCommonData) diff --git a/apps/web/src/actions/teams.ts b/apps/web/src/actions/teams.ts index 6adc78d0..a257024c 100644 --- a/apps/web/src/actions/teams.ts +++ b/apps/web/src/actions/teams.ts @@ -1,20 +1,26 @@ "use server"; // TODO: update team /api endpoints to be actions - import { authenticatedAction } from "@/lib/safe-action"; -import { z } from "zod"; +import { boolean, string, z } from "zod"; import { db } from "db"; import { userHackerData, teams, invites } from "db/schema"; import { eq } from "db/drizzle"; import { revalidatePath } from "next/cache"; import { getHacker } from "db/functions"; +import { returnValidationErrors } from "next-safe-action"; -export const leaveTeam = authenticatedAction( - z.null(), - async (_, { userId }) => { +export const leaveTeam = authenticatedAction + .outputSchema( + z.object({ + success: z.boolean(), + message: z.string(), + }), + ) + .action(async ({ ctx: { userId } }) => { const user = await getHacker(userId, false); - if (!user) throw new Error("User not found"); + if (!user) + returnValidationErrors(z.null(), { _errors: ["User not found"] }); if (!user.hackerData.teamID) { revalidatePath("/dash/team"); @@ -78,5 +84,4 @@ export const leaveTeam = authenticatedAction( }); return result; - }, -); + }); diff --git a/apps/web/src/actions/user-profile-mod.ts b/apps/web/src/actions/user-profile-mod.ts index fb6eb032..ee604ca7 100644 --- a/apps/web/src/actions/user-profile-mod.ts +++ b/apps/web/src/actions/user-profile-mod.ts @@ -3,65 +3,220 @@ import { authenticatedAction } from "@/lib/safe-action"; import { z } from "zod"; import { db } from "db"; -import { userCommonData } from "db/schema"; +import { userCommonData, userHackerData } from "db/schema"; import { eq } from "db/drizzle"; import { put } from "@vercel/blob"; import { decodeBase64AsFile } from "@/lib/utils/shared/files"; +import { returnValidationErrors } from "next-safe-action"; import { revalidatePath } from "next/cache"; -import { getUser } from "db/functions"; +import { getUser, getUserByTag } from "db/functions"; +import { RegistrationSettingsFormValidator } from "@/validators/shared/RegistrationSettingsForm"; -// TODO: Add skill updating -export const modifyRegistrationData = authenticatedAction( - z.object({ - bio: z.string().max(500), - skills: z.string().max(100), - }), - async ({ bio, skills }, { userId }) => { - const user = await getUser(userId); - if (!user) throw new Error("User not found"); +export const modifyRegistrationData = authenticatedAction + .schema(RegistrationSettingsFormValidator) + .action( + async ({ + parsedInput: { + age, + gender, + race, + ethnicity, + isEmailable, + university, + major, + levelOfStudy, + schoolID, + hackathonsAttended, + softwareBuildingExperience, + heardAboutEvent, + shirtSize, + dietaryRestrictions, + accommodationNote, + github, + linkedin, + personalWebsite, + phoneNumber, + countryOfResidence, + }, + ctx: { userId }, + }) => { + const user = await getUser(userId); + if (!user) throw new Error("User not found"); + await Promise.all([ + db + .update(userCommonData) + .set({ + age, + gender, + race, + ethnicity, + shirtSize, + dietRestrictions: dietaryRestrictions, + accommodationNote, + phoneNumber, + countryOfResidence, + }) + .where(eq(userCommonData.clerkID, user.clerkID)), + db + .update(userHackerData) + .set({ + isEmailable, + university, + major, + levelOfStudy, + schoolID, + hackathonsAttended, + softwareExperience: softwareBuildingExperience, + heardFrom: heardAboutEvent, + GitHub: github, + LinkedIn: linkedin, + PersonalWebsite: personalWebsite, + }) + .where(eq(userHackerData.clerkID, user.clerkID)), + ]); + return { + success: true, + newAge: age, + newGender: gender, + newRace: race, + newEthnicity: ethnicity, + newWantsToReceiveMLHEmails: isEmailable, + newUniversity: university, + newMajor: major, + newLevelOfStudy: levelOfStudy, + newSchoolID: schoolID, + newHackathonsAttended: hackathonsAttended, + newSoftwareExperience: softwareBuildingExperience, + newHeardFrom: heardAboutEvent, + newShirtSize: shirtSize, + newDietaryRestrictions: dietaryRestrictions, + newAccommodationNote: accommodationNote, + newGitHub: github, + newLinkedIn: linkedin, + newPersonalWebsite: personalWebsite, + newPhoneNumber: phoneNumber, + newCountryOfResidence: countryOfResidence, + }; + }, + ); +export const modifyResume = authenticatedAction + .schema( + z.object({ + resume: z.string(), + }), + ) + .action(async ({ parsedInput: { resume }, ctx: { userId } }) => { await db - .update(userCommonData) - .set({ bio }) - .where(eq(userCommonData.clerkID, user.clerkID)); - return { success: true, newbio: bio }; - }, -); - -export const modifyAccountSettings = authenticatedAction( - z.object({ - firstName: z.string().min(1).max(50), - lastName: z.string().min(1).max(50), - }), - async ({ firstName, lastName }, { userId }) => { - const user = await getUser(userId); - if (!user) throw new Error("User not found"); - - await db - .update(userCommonData) - .set({ firstName, lastName }) - .where(eq(userCommonData.clerkID, userId)); + .update(userHackerData) + .set({ resume }) + .where(eq(userHackerData.clerkID, userId)); return { success: true, - newFirstName: firstName, - newLastName: lastName, + newResume: resume, }; - }, -); + }); -export const updateProfileImage = authenticatedAction( - z.object({ fileBase64: z.string(), fileName: z.string() }), - async ({ fileBase64, fileName }, { userId }) => { - const image = await decodeBase64AsFile(fileBase64, fileName); - const user = await getUser(userId); - if (!user) throw new Error("User not found"); +export const modifyProfileData = authenticatedAction + .schema( + z.object({ + pronouns: z.string(), + bio: z.string(), + skills: z.string().array(), + discord: z.string(), + }), + ) + .action( + async ({ + parsedInput: { bio, discord, pronouns, skills }, + ctx: { userId }, + }) => { + const user = await getUser(userId); + if (!user) { + throw new Error("User not found"); + } + await db + .update(userCommonData) + .set({ pronouns, bio, skills, discord }) + .where(eq(userCommonData.clerkID, user.clerkID)); + return { + success: true, + newPronouns: pronouns, + newBio: bio, + newSkills: skills, + newDiscord: discord, + }; + }, + ); - const blobUpload = await put(image.name, image, { access: "public" }); - await db - .update(userCommonData) - .set({ profilePhoto: blobUpload.url }) - .where(eq(userCommonData.clerkID, user.clerkID)); - revalidatePath("/settings/profile"); - return { success: true }; - }, -); +// TODO: Fix after registration enhancements to allow for failure on conflict and return appropriate error message +export const modifyAccountSettings = authenticatedAction + .schema( + z.object({ + firstName: z.string().min(1).max(50), + lastName: z.string().min(1).max(50), + hackerTag: z.string().min(1).max(50), + hasSearchableProfile: z.boolean(), + }), + ) + .action( + async ({ + parsedInput: { + firstName, + lastName, + hackerTag, + hasSearchableProfile, + }, + ctx: { userId }, + }) => { + const user = await getUser(userId); + if (!user) throw new Error("User not found"); + let oldHackerTag = user.hackerTag; // change when hackertag is not PK on profileData table + if (oldHackerTag != hackerTag) + if (await getUserByTag(hackerTag)) + //if hackertag changed + // copied from /api/registration/create + return { + success: false, + message: "hackertag_not_unique", + }; + await db + .update(userCommonData) + .set({ + firstName, + lastName, + hackerTag, + isSearchable: hasSearchableProfile, + }) + .where(eq(userCommonData.clerkID, userId)); + return { + success: true, + newFirstName: firstName, + newLastName: lastName, + newHackerTag: hackerTag, + newHasSearchableProfile: hasSearchableProfile, + }; + }, + ); + +export const updateProfileImage = authenticatedAction + .schema(z.object({ fileBase64: z.string(), fileName: z.string() })) + .action( + async ({ parsedInput: { fileBase64, fileName }, ctx: { userId } }) => { + const image = await decodeBase64AsFile(fileBase64, fileName); + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), + }); + if (!user) throw new Error("User not found"); + + const blobUpload = await put(image.name, image, { + access: "public", + }); + await db + .update(userCommonData) + .set({ profilePhoto: blobUpload.url }) + .where(eq(userCommonData.clerkID, user.clerkID)); + revalidatePath("/settings#profile"); + return { success: true }; + }, + ); diff --git a/apps/web/src/app/admin/check-in/page.tsx b/apps/web/src/app/admin/check-in/page.tsx index 0dea1ab8..af117b82 100644 --- a/apps/web/src/app/admin/check-in/page.tsx +++ b/apps/web/src/app/admin/check-in/page.tsx @@ -19,7 +19,8 @@ export default async function Page({ ); const scanUser = await getUser(searchParams.user); - if (!scanUser) + console.log(scanUser); + if (!scanUser) { return (
); + } return (
diff --git a/apps/web/src/app/admin/page.tsx b/apps/web/src/app/admin/page.tsx index 67a62997..fee8a3bc 100644 --- a/apps/web/src/app/admin/page.tsx +++ b/apps/web/src/app/admin/page.tsx @@ -33,7 +33,7 @@ export default async function Page() {

Welcome,

-

+

{adminUser.firstName}

diff --git a/apps/web/src/app/api/admin/events/create/route.ts b/apps/web/src/app/api/admin/events/create/route.ts index 6c345aab..14f2fab5 100644 --- a/apps/web/src/app/api/admin/events/create/route.ts +++ b/apps/web/src/app/api/admin/events/create/route.ts @@ -37,6 +37,7 @@ export async function POST(req: Request) { description: parsedBody.data.description, startTime: parsedBody.data.startTime, endTime: parsedBody.data.endTime, + location: parsedBody.data.location, type: parsedBody.data.type, host: parsedBody.data.host && parsedBody.data.host.length > 0 diff --git a/apps/web/src/app/dash/page.tsx b/apps/web/src/app/dash/page.tsx index e7fc5e28..579d6f11 100644 --- a/apps/web/src/app/dash/page.tsx +++ b/apps/web/src/app/dash/page.tsx @@ -27,7 +27,7 @@ export default async function Page() {

Welcome,

-

+

{user.firstName}

diff --git a/apps/web/src/app/dash/pass/page.tsx b/apps/web/src/app/dash/pass/page.tsx index 0dc16f3f..0c9016ae 100644 --- a/apps/web/src/app/dash/pass/page.tsx +++ b/apps/web/src/app/dash/pass/page.tsx @@ -63,7 +63,7 @@ function EventPass({ qrPayload, user, clerk, guild }: EventPassProps) {

{user.firstName}

-
+

@{user.hackerTag}

diff --git a/apps/web/src/app/dash/schedule/page.tsx b/apps/web/src/app/dash/schedule/page.tsx index 1d9b20fc..5db45ab7 100644 --- a/apps/web/src/app/dash/schedule/page.tsx +++ b/apps/web/src/app/dash/schedule/page.tsx @@ -1,11 +1,61 @@ import { Suspense } from "react"; import UserScheduleView from "@/components/schedule/UserScheduleView"; +import ScheduleTimeline from "./schedule-timeline"; import Loading from "@/components/shared/Loading"; -export default function Page() { +import { getAllEvents } from "db/functions"; +import { headers } from "next/headers"; +import { VERCEL_IP_TIMEZONE_HEADER_KEY } from "@/lib/constants"; +import { getClientTimeZone } from "@/lib/utils/client/shared"; +import { Badge } from "@/components/shadcn/ui/badge"; +import c from "config"; +export default async function Page() { + const sched = await getAllEvents(); + + const currentEvent = sched.filter((e) => { + const currentTime = new Date(); + return e.startTime < currentTime && e.endTime > currentTime; + })[0]; + const userTimeZoneHeaderKey = headers().get(VERCEL_IP_TIMEZONE_HEADER_KEY); + const userTimeZone = getClientTimeZone(userTimeZoneHeaderKey); return ( - }> - - + <> +
+

+ Schedule +

+
+

Current

+
+

+ {currentEvent?.title || "No current events"}{" "} + {currentEvent && ( + + )[currentEvent?.type], + }} + > +

+ {currentEvent?.type} +

+ + )} +

+
+
+
+ + }> + {/* */} + + + ); } diff --git a/apps/web/src/app/dash/schedule/schedule-timeline.tsx b/apps/web/src/app/dash/schedule/schedule-timeline.tsx new file mode 100644 index 00000000..7ff2d337 --- /dev/null +++ b/apps/web/src/app/dash/schedule/schedule-timeline.tsx @@ -0,0 +1,153 @@ +"use client"; +import { Badge } from "@/components/shadcn/ui/badge"; +import { type EventType as Event } from "@/lib/types/events"; +import { cn } from "@/lib/utils/client/cn"; +import c from "config"; +import { formatInTimeZone } from "date-fns-tz"; +import Link from "next/link"; +import { ReactNode } from "react"; + +const daysOfWeek = [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", +]; + +function splitByDay(schedule: Event[]) { + const days: Map = new Map(); + schedule.forEach((event) => { + const day = daysOfWeek[event.startTime.getDay()]; + if (days.get(day)) { + days.get(day)?.push(event); + } else { + days.set(day, [event]); + } + }); + return days; +} + +type ScheduleTimelineProps = { + schedule: Event[]; + timezone: string; +}; +export default function ScheduleTimeline({ + schedule, + timezone, +}: ScheduleTimelineProps) { + return ( +
+ + + {Array.from(splitByDay(schedule).entries()).map( + ([dayName, arr]): ReactNode => ( + <> + + + + + + {arr?.map( + (event): ReactNode => ( + + ), + )} + + ), + )} + +
+

+ {dayName} +

+
+
+ ); +} + +type EventRowProps = { event: Event; userTimeZone: string }; +export function EventRow({ event, userTimeZone }: EventRowProps) { + const startTimeFormatted = formatInTimeZone( + event.startTime, + userTimeZone, + "hh:mm a", + { + useAdditionalDayOfYearTokens: true, + }, + ); + + const endTimeFormatted = formatInTimeZone( + event.endTime, + userTimeZone, + "h:mm a", + ); + + const currentTime = new Date(); + const isLive = event.startTime < currentTime && event.endTime > currentTime; + + const href = `/schedule/${event.id}`; + const color = (c.eventTypes as Record)[event.type]; + return ( + + + {`${startTimeFormatted} - ${endTimeFormatted}`} + + {isLive ? ( +
+ ) : ( +
+
+
+ )} + + +
+ {event.title}{" "} + +

{event.type}

+
+
+ + + + ); +} diff --git a/apps/web/src/app/discord-verify/linked/page.tsx b/apps/web/src/app/discord-verify/linked/page.tsx index c5874538..5c50965a 100644 --- a/apps/web/src/app/discord-verify/linked/page.tsx +++ b/apps/web/src/app/discord-verify/linked/page.tsx @@ -19,7 +19,7 @@ export default function Page() { To unlink, go to your {c.hackathonName} account settings to unlink before linking a new one.

- +
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index 9755fa02..58759f96 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -38,10 +38,10 @@ --radius: 0.5rem; - --gradient-color-1: #668cff; - --gradient-color-2: #3366ff; - --gradient-color-3: #002db3; - --gradient-color-4: #1952cc; + --gradient-color-1: #3f63c8; + --gradient-color-2: #1a46b3; + --gradient-color-3: #001773; + --gradient-color-4: #11377d; } .dark { @@ -82,6 +82,7 @@ * { @apply border-border; } + body { @apply bg-background text-foreground; } @@ -141,3 +142,46 @@ .register-clip { clip-path: polygon(10% 0, 100% 0, 90% 100%, 0 100%); } + +.pulsatingDot { + /* animation: pulseDot 1.25s cubic-bezier(0.455, 0.03, 0.515, 0.955) -.4s infinite; */ + /* border-radius: 50%; */ + /* box-sizing: border-box; */ + transform-origin: center; +} + +.pulsatingDot:before { + animation: pulseRing 1.25s cubic-bezier(0.215, 0.61, 0.355, 1) infinite; + /* background-color: var(--pulsating-dot, #00BEFF); */ + background-color: hsl(var(--hackathon-primary)); + border-radius: 45px; + content: ""; + display: block; + height: 300%; + left: -100%; + position: relative; + top: -100%; + width: 300%; +} + +@keyframes pulseRing { + 0% { + transform: scale(0.5); + } + + 80%, + 100% { + opacity: 0; + } +} + +@keyframes pulseDot { + 0%, + 100% { + transform: scale(1); + } + + 50% { + transform: scale(1.1); + } +} diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 9e681b85..f5908247 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -5,8 +5,9 @@ import About from "@/components/landing/About"; import Partners from "@/components/landing/Partners"; import Footer from "@/components/landing/Footer"; +import { Sponsors } from "@/components/landing/Sponsors"; import MLHBadge from "@/components/landing/MLHBadge"; - +import Team from "@/components/landing/Team"; import { Oswald } from "next/font/google"; import WorkWithUs from "@/components/landing/WorkWithUs"; import TrailerSection from "@/components/landing/TrailerSection"; @@ -52,8 +53,10 @@ export default function Home() {
+ {/* */} +
diff --git a/apps/web/src/app/schedule/page.tsx b/apps/web/src/app/schedule/page.tsx new file mode 100644 index 00000000..868914a7 --- /dev/null +++ b/apps/web/src/app/schedule/page.tsx @@ -0,0 +1,68 @@ +import { Suspense } from "react"; +import UserScheduleView from "@/components/schedule/UserScheduleView"; +import ScheduleTimeline from "../dash/schedule/schedule-timeline"; +import Loading from "@/components/shared/Loading"; +import { getAllEvents } from "db/functions"; +import { headers } from "next/headers"; +import { VERCEL_IP_TIMEZONE_HEADER_KEY } from "@/lib/constants"; +import { getClientTimeZone } from "@/lib/utils/client/shared"; +import { Badge } from "@/components/shadcn/ui/badge"; +import c from "config"; +import { Skeleton } from "@/components/shadcn/ui/skeleton"; +import Navbar from "@/components/shared/Navbar"; +export default async function Page() { + const sched = await getAllEvents(); + + const currentEvent = sched.filter((e) => { + const currentTime = new Date(); + return e.startTime < currentTime && e.endTime > currentTime; + })[0]; + const userTimeZoneHeaderKey = headers().get(VERCEL_IP_TIMEZONE_HEADER_KEY); + const userTimeZone = getClientTimeZone(userTimeZoneHeaderKey); + return ( + <> + }> + + +
+

+ Schedule +

+
+

Current

+
+

+ {currentEvent?.title || "No current events"}{" "} + {currentEvent && ( + + )[currentEvent?.type], + }} + > +

+ {currentEvent?.type} +

+ + )} +

+
+
+
+ + }> + {/* */} + + + + ); +} + +export const runtime = "edge"; +export const revalidate = 60; diff --git a/apps/web/src/app/settings/account/page.tsx b/apps/web/src/app/settings/account/page.tsx deleted file mode 100644 index fbfdd1e0..00000000 --- a/apps/web/src/app/settings/account/page.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import AccountSettings from "@/components/settings/AccountSettings"; -import { auth } from "@clerk/nextjs"; -import { redirect } from "next/navigation"; -import { getHacker } from "db/functions"; - -export default async function Page() { - const { userId } = auth(); - if (!userId) return redirect("/sign-in"); - - const user = await getHacker(userId, false); - if (!user) return redirect("/sign-in"); - return ; -} - -export const runtime = "edge"; diff --git a/apps/web/src/app/settings/layout.tsx b/apps/web/src/app/settings/layout.tsx index f0497ab5..560f9dd9 100644 --- a/apps/web/src/app/settings/layout.tsx +++ b/apps/web/src/app/settings/layout.tsx @@ -34,12 +34,15 @@ export default async function ({ children }: { children: ReactNode }) {
-
- {/* */} - - -
-
{children}
+ +
{children}
); diff --git a/apps/web/src/app/settings/page.tsx b/apps/web/src/app/settings/page.tsx index 9c4722c4..685c8251 100644 --- a/apps/web/src/app/settings/page.tsx +++ b/apps/web/src/app/settings/page.tsx @@ -1,3 +1,32 @@ -export default function Page() { - return howdy; +import AccountSettings from "@/components/settings/AccountSettings"; +import { auth } from "@clerk/nextjs"; +import { redirect } from "next/navigation"; +import ProfileSettings from "@/components/settings/ProfileSettings"; +import RegistrationSettings from "@/components/settings/RegistrationSettings"; +import { getUser } from "db/functions"; + +export default async function Page() { + const { userId } = auth(); + if (!userId) return redirect("/sign-in"); + const user = await getUser(userId); + if (!user) return redirect("/sign-in"); + + return ( +
+
+ +
+ +
+ +
+ ); +} + +function Header({ tag }: { tag: string }) { + return ( +

+ {tag} +

+ ); } diff --git a/apps/web/src/app/settings/profile/page.tsx b/apps/web/src/app/settings/profile/page.tsx deleted file mode 100644 index 06e9a82d..00000000 --- a/apps/web/src/app/settings/profile/page.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import ProfileSettings from "@/components/settings/ProfileSettings"; -import { auth } from "@clerk/nextjs"; -import { getHacker } from "db/functions"; - -export default async function Page() { - const { userId } = auth(); - if (!userId) throw new Error("User not found"); - - const user = await getHacker(userId, false); - if (!user) throw new Error("User not found"); - return ( - - ); -} - -export const runtime = "edge"; diff --git a/apps/web/src/app/settings/registration/page.tsx b/apps/web/src/app/settings/registration/page.tsx new file mode 100644 index 00000000..ba11a134 --- /dev/null +++ b/apps/web/src/app/settings/registration/page.tsx @@ -0,0 +1,15 @@ +import { auth } from "@clerk/nextjs"; +import { redirect } from "next/navigation"; +import RegistrationFormSettings from "@/components/settings/RegistrationForm/RegisterFormSettings"; +import { getHackerData, getUser } from "db/functions"; + +export default async function Page() { + const { userId } = auth(); + if (!userId) return redirect("/sign-in"); + const user = await getUser(userId); + if (!user) return redirect("/sign-in"); + const hackerData = await getHackerData(userId); + if (!hackerData) return redirect("/sign-in"); + + return ; +} diff --git a/apps/web/src/components/admin/scanner/CheckinScanner.tsx b/apps/web/src/components/admin/scanner/CheckinScanner.tsx index b07abc5a..525ad748 100644 --- a/apps/web/src/components/admin/scanner/CheckinScanner.tsx +++ b/apps/web/src/components/admin/scanner/CheckinScanner.tsx @@ -3,11 +3,11 @@ import { useState, useEffect } from "react"; import { QrScanner } from "@yudiel/react-qr-scanner"; import superjson from "superjson"; -import { checkInUser } from "@/actions/admin/scanner-admin-actions"; -import { useAction } from "next-safe-action/hook"; +import { checkInUserToHackathon } from "@/actions/admin/scanner-admin-actions"; import { type QRDataInterface } from "@/lib/utils/shared/qr"; import type { User } from "db/types"; - +import clsx from "clsx"; +import { useAction } from "next-safe-action/hooks"; import { Drawer, DrawerContent, @@ -19,16 +19,8 @@ import { import { Button } from "@/components/shadcn/ui/button"; import { useRouter, usePathname, useSearchParams } from "next/navigation"; import { toast } from "sonner"; - -/* - -Pass Scanner Props: - -eventName: name of the event that the user is scanning into -hasScanned: if the state has eventered one in which a QR has been scanned (whether that scan has scanned before or not) -scan: the scan object that has been scanned. If they have not scanned before scan will be null leading to a new record or if they have then it will incriment the scan count. - -*/ +import { FIVE_MINUTES_IN_MILLISECONDS } from "@/lib/constants"; +import { ValidationErrors } from "next-safe-action"; interface CheckinScannerProps { hasScanned: boolean; @@ -43,9 +35,9 @@ export default function CheckinScanner({ scanUser, hasRSVP, }: CheckinScannerProps) { + console.log("scanner props is: ", hasScanned, checkedIn, scanUser, hasRSVP); + const [scanLoading, setScanLoading] = useState(false); - const { execute: runScanAction } = useAction(checkInUser, {}); - const [proceed, setProceed] = useState(hasRSVP); useEffect(() => { if (hasScanned) { setScanLoading(false); @@ -56,22 +48,83 @@ export default function CheckinScanner({ const path = usePathname(); const router = useRouter(); + function handleUseActionFeedback(hasErrored = false, message = "") { + console.log("called"); + toast.dismiss(); + hasErrored + ? toast.error(message || "Failed to Check User Into Hackathon") + : toast.success( + message || "Successfully Checked User Into Hackathon!", + ); + router.replace(`${path}`); + } + + const { execute: runCheckInUserToHackathon } = useAction( + checkInUserToHackathon, + { + onSuccess: () => { + handleUseActionFeedback(); + }, + onError: ({ error, input }) => { + console.log("error is: ", error); + console.log("input is: ", input); + if (error.validationErrors?.QRTimestamp?._errors) { + handleUseActionFeedback( + true, + error.validationErrors.QRTimestamp._errors[0], + ); + } else { + handleUseActionFeedback(true); + } + }, + }, + ); + function handleScanCreate() { const params = new URLSearchParams(searchParams.toString()); const timestamp = parseInt(params.get("createdAt") as string); + + if (!scanUser) { + return alert("User Not Found"); + } + if (isNaN(timestamp)) { return alert("Invalid QR Code Data (Field: createdAt)"); } + if (Date.now() - timestamp > FIVE_MINUTES_IN_MILLISECONDS) { + return alert( + "QR Code has expired. Please tell user to refresh the QR Code", + ); + } + if (checkedIn) { return alert("User Already Checked in!"); } else { - // TODO: make this a little more typesafe - runScanAction(scanUser?.clerkID!); + toast.loading("Checking User In"); + runCheckInUserToHackathon({ + userID: scanUser.clerkID, + QRTimestamp: timestamp, + }); } - toast.success("Successfully Scanned User In"); - router.replace(`${path}`); + router.replace(path); } + const drawerTitle = checkedIn + ? "User Already Checked In" + : !hasRSVP + ? "Warning!" + : "New Scan"; + const drawerDescription = checkedIn + ? "If this is a mistake, please talk to an admin" + : !hasRSVP + ? `${scanUser?.firstName} ${scanUser?.lastName} Is not RSVP'd` + : `New scan for ${scanUser?.firstName} ${scanUser?.lastName}`; + const drawerFooterButtonText = checkedIn + ? "Close" + : !hasRSVP + ? "Check In Anyways" + : "Scan User In"; + return ( <>
@@ -108,11 +161,6 @@ export default function CheckinScanner({ }} />
- {/*
- - - -
*/} Loading Scan... - {/* */} - - - - ) : ( - <> - - New Scan - - - New scan for{" "} - {scanUser?.firstName}{" "} - {scanUser?.lastName} - - - )} - - )} + + {drawerTitle} + - {proceed ? ( - <> - - {!checkedIn && ( - - )} - - - - ) : ( - <> - )} + + {drawerDescription} + + + {!hasRSVP && !checkedIn && ( +
+ Do you wish to proceed? +
+ )} + {!checkedIn && ( + + )} + +
)} diff --git a/apps/web/src/components/admin/scanner/PassScanner.tsx b/apps/web/src/components/admin/scanner/PassScanner.tsx index adf5bf63..0c3214d9 100644 --- a/apps/web/src/components/admin/scanner/PassScanner.tsx +++ b/apps/web/src/components/admin/scanner/PassScanner.tsx @@ -4,7 +4,7 @@ import { useState, useEffect } from "react"; import { QrScanner } from "@yudiel/react-qr-scanner"; import superjson from "superjson"; import { createScan } from "@/actions/admin/scanner-admin-actions"; -import { useAction } from "next-safe-action/hook"; +import { useAction } from "next-safe-action/hooks"; import { type QRDataInterface } from "@/lib/utils/shared/qr"; import type { Scan, Event, Hacker } from "db/types"; import c from "config"; diff --git a/apps/web/src/components/admin/toggles/NavItemsManager.tsx b/apps/web/src/components/admin/toggles/NavItemsManager.tsx index ca6c230c..a24784c3 100644 --- a/apps/web/src/components/admin/toggles/NavItemsManager.tsx +++ b/apps/web/src/components/admin/toggles/NavItemsManager.tsx @@ -24,7 +24,7 @@ import { Input } from "@/components/shadcn/ui/input"; import { Label } from "@/components/shadcn/ui/label"; import { Plus } from "lucide-react"; import { useState } from "react"; -import { useAction, useOptimisticAction } from "next-safe-action/hook"; +import { useAction, useOptimisticAction } from "next-safe-action/hooks"; import { setItem, removeItem, @@ -108,17 +108,16 @@ function ToggleSwitch({ }) { const initialData = { itemStatus }; // Initial data matching the shape of toggleItem's return type - const { execute, optimisticData } = useOptimisticAction( - toggleItem, - initialData, - (state, { statusToSet }) => { + const { execute, optimisticState } = useOptimisticAction(toggleItem, { + currentState: initialData, + updateFn: (state, { statusToSet }) => { return { itemStatus: statusToSet }; }, - ); + }); return ( execute({ name, statusToSet: checked }) } diff --git a/apps/web/src/components/admin/toggles/RegistrationSettings.tsx b/apps/web/src/components/admin/toggles/RegistrationSettings.tsx index 180fdd12..a96ffa19 100644 --- a/apps/web/src/components/admin/toggles/RegistrationSettings.tsx +++ b/apps/web/src/components/admin/toggles/RegistrationSettings.tsx @@ -4,7 +4,7 @@ import { Button } from "@/components/shadcn/ui/button"; import { Input } from "@/components/shadcn/ui/input"; import { Label } from "@/components/shadcn/ui/label"; import { Switch } from "@/components/shadcn/ui/switch"; -import { useOptimisticAction } from "next-safe-action/hook"; +import { useOptimisticAction } from "next-safe-action/hooks"; import { toast } from "sonner"; import { toggleRegistrationEnabled, @@ -26,36 +26,36 @@ export function RegistrationToggles({ }: RegistrationTogglesProps) { const { execute: executeToggleSecretRegistrationEnabled, - optimisticData: ToggleSecretRegistrationEnabledOptimisticData, - } = useOptimisticAction( - toggleSecretRegistrationEnabled, - { success: true, statusSet: defaultSecretRegistrationEnabled }, - (state, { enabled }) => { + optimisticState: ToggleSecretRegistrationEnabledOptimisticData, + } = useOptimisticAction(toggleSecretRegistrationEnabled, { + currentState: { + success: true, + statusSet: defaultSecretRegistrationEnabled, + }, + updateFn: (state, { enabled }) => { return { statusSet: enabled, success: true }; }, - ); + }); const { execute: executeToggleRSVPs, - optimisticData: toggleRSVPsOptimisticData, - } = useOptimisticAction( - toggleRSVPs, - { success: true, statusSet: defaultRSVPsEnabled }, - (state, { enabled }) => { + optimisticState: toggleRSVPsOptimisticData, + } = useOptimisticAction(toggleRSVPs, { + currentState: { success: true, statusSet: defaultRSVPsEnabled }, + updateFn: (state, { enabled }) => { return { statusSet: enabled, success: true }; }, - ); + }); const { execute: executeToggleRegistrationEnabled, - optimisticData: ToggleRegistrationEnabledOptimisticData, - } = useOptimisticAction( - toggleRegistrationEnabled, - { success: true, statusSet: defaultRegistrationEnabled }, - (state, { enabled }) => { + optimisticState: ToggleRegistrationEnabledOptimisticData, + } = useOptimisticAction(toggleRegistrationEnabled, { + currentState: { success: true, statusSet: defaultRegistrationEnabled }, + updateFn: (state, { enabled }) => { return { statusSet: enabled, success: true }; }, - ); + }); return ( <> diff --git a/apps/web/src/components/admin/users/ApproveUserButton.tsx b/apps/web/src/components/admin/users/ApproveUserButton.tsx index 136cdc29..7267cc6b 100644 --- a/apps/web/src/components/admin/users/ApproveUserButton.tsx +++ b/apps/web/src/components/admin/users/ApproveUserButton.tsx @@ -1,7 +1,7 @@ "use client"; import { Button } from "@/components/shadcn/ui/button"; -import { useAction } from "next-safe-action/hook"; +import { useAction } from "next-safe-action/hooks"; import { setUserApproval } from "@/actions/admin/user-actions"; import { toast } from "sonner"; diff --git a/apps/web/src/components/admin/users/UpdateRoleDialog.tsx b/apps/web/src/components/admin/users/UpdateRoleDialog.tsx index edb2d349..edf59a25 100644 --- a/apps/web/src/components/admin/users/UpdateRoleDialog.tsx +++ b/apps/web/src/components/admin/users/UpdateRoleDialog.tsx @@ -18,7 +18,7 @@ import { import { Button } from "@/components/shadcn/ui/button"; import { perms } from "config"; import { toast } from "sonner"; -import { useAction } from "next-safe-action/hook"; +import { useAction } from "next-safe-action/hooks"; import { updateRole } from "@/actions/admin/user-actions"; import { useState } from "react"; import { titleCase } from "title-case"; diff --git a/apps/web/src/components/dash/overview/ClientBubbles.tsx b/apps/web/src/components/dash/overview/ClientBubbles.tsx index 8551b01b..ba47cb0d 100644 --- a/apps/web/src/components/dash/overview/ClientBubbles.tsx +++ b/apps/web/src/components/dash/overview/ClientBubbles.tsx @@ -54,7 +54,7 @@ export function Countdown({ title, date }: CountdownProps) {
- + Time To {title}
diff --git a/apps/web/src/components/dash/overview/ServerBubbles.tsx b/apps/web/src/components/dash/overview/ServerBubbles.tsx index b694789f..66847c37 100644 --- a/apps/web/src/components/dash/overview/ServerBubbles.tsx +++ b/apps/web/src/components/dash/overview/ServerBubbles.tsx @@ -29,13 +29,16 @@ export function Questions() { export function TitleBubble() { return ( -
+
-
-

+
+ {/*

{c.hackathonName} +

*/} +

+ {"rowdyhacks"}

-

+

{`${format(c.startDate, "h:mma, MMM d, yyyy")}`} @{" "} {c.prettyLocation}

diff --git a/apps/web/src/components/dash/shared/ProfileButton.tsx b/apps/web/src/components/dash/shared/ProfileButton.tsx index 3b47e860..2ae43017 100644 --- a/apps/web/src/components/dash/shared/ProfileButton.tsx +++ b/apps/web/src/components/dash/shared/ProfileButton.tsx @@ -115,7 +115,7 @@ export default async function ProfileButton() { Report a Bug - + Settings diff --git a/apps/web/src/components/dash/team/LeaveTeamButton.tsx b/apps/web/src/components/dash/team/LeaveTeamButton.tsx index 30aef26e..5b45a378 100644 --- a/apps/web/src/components/dash/team/LeaveTeamButton.tsx +++ b/apps/web/src/components/dash/team/LeaveTeamButton.tsx @@ -3,7 +3,7 @@ import { LogOut } from "lucide-react"; import { Button } from "@/components/shadcn/ui/button"; import { leaveTeam } from "@/actions/teams"; -import { useAction } from "next-safe-action/hook"; +import { useAction } from "next-safe-action/hooks"; import { toast } from "sonner"; interface LeaveTeamButtonProps { @@ -12,22 +12,29 @@ interface LeaveTeamButtonProps { export default function LeaveTeamButton({ issueEmail }: LeaveTeamButtonProps) { const { execute: runLeaveTeam, status } = useAction(leaveTeam, { - onSuccess: ({ success, message }) => { - toast.dismiss(); - if (success) { - toast.success(message); + onSuccess: ({ data }) => { + if (data) { + if (data.success) { + toast.success(data.message); + } else { + toast.error(data.message); + } } else { - toast.error(message); + toast.dismiss(); + toast.error( + `An unknown error occured. If this persists, please email ${issueEmail}.`, + ); } + toast.dismiss(); }, - onError: (error) => { + onError: ({ error }) => { toast.dismiss(); toast.error( `An unknown error occured. If this persists, please email ${issueEmail}.`, ); - console.error("Fetch Error: ", error.fetchError); + // console.error("Fetch Error: ", error.fetchError); console.error("Server Error: ", error.serverError); - console.error("Validation Error: ", error.validationError); + console.error("Validation Error: ", error.validationErrors); }, }); @@ -35,7 +42,7 @@ export default function LeaveTeamButton({ issueEmail }: LeaveTeamButtonProps) { toast.loading("Leaving team...", { duration: 0, }); - runLeaveTeam(null); + runLeaveTeam(); } return ( diff --git a/apps/web/src/components/events/admin/EventDetails.tsx b/apps/web/src/components/events/admin/EventDetails.tsx index 1bbeed20..6d48b0a7 100644 --- a/apps/web/src/components/events/admin/EventDetails.tsx +++ b/apps/web/src/components/events/admin/EventDetails.tsx @@ -52,7 +52,12 @@ export default function EventFull({ event }: { event: Event }) {

Hosted by {event.host}

-

Description:

+

+ Location:{" "} + {event.location} +

+ +

Description

{event.description}

diff --git a/apps/web/src/components/events/admin/NewEventForm.tsx b/apps/web/src/components/events/admin/NewEventForm.tsx index 290477ad..7f6aba47 100644 --- a/apps/web/src/components/events/admin/NewEventForm.tsx +++ b/apps/web/src/components/events/admin/NewEventForm.tsx @@ -48,6 +48,8 @@ export default function NewEventForm({ defaultDate }: NewEventFormProps) { type: "" as any, host: "", startTime: defaultDate, + points: 0, + location: "TBD", endTime: new Date(defaultDate.getTime() + ONE_HOUR_IN_MILLISECONDS), }, }); @@ -92,6 +94,7 @@ export default function NewEventForm({ defaultDate }: NewEventFormProps) { )} /> + )} /> + ( + + Location + + + + {/* + Keep title short and concise + */} + + + )} + />
Event Start { - const newDate = !!date + const newDate = date ? date.toDate(userLocalTimeZone) : null; field.onChange(newDate); diff --git a/apps/web/src/components/events/shared/EventColumns.tsx b/apps/web/src/components/events/shared/EventColumns.tsx index 48a3500b..7a9b5751 100644 --- a/apps/web/src/components/events/shared/EventColumns.tsx +++ b/apps/web/src/components/events/shared/EventColumns.tsx @@ -29,6 +29,11 @@ export const columns: ColumnDef[] = [ ), }, + { + accessorKey: "location", + header: "Location", + cell: ({ row }) => {row.original.location}, + }, { accessorKey: "startTime", header: "Start", diff --git a/apps/web/src/components/landing/Person.tsx b/apps/web/src/components/landing/Person.tsx index 83e976af..5254d5e1 100644 --- a/apps/web/src/components/landing/Person.tsx +++ b/apps/web/src/components/landing/Person.tsx @@ -1,9 +1,21 @@ -export type Person = { - fname: string; //picture file name must match name with .png - lname: string; - imgLink: string; - role: string; +type Picture = { + link: string; + zoom: number; + x: number; + y: number; +}; + +type Person = { + name: string; + nameSize: number; + team: string; + professionalPicture: Picture; + personalityPicture: Picture; linkedin: string; - website: string; github: string; + personal: string; +}; + +type Team = { + team: Person[]; }; diff --git a/apps/web/src/components/landing/SponsorItem.tsx b/apps/web/src/components/landing/SponsorItem.tsx new file mode 100644 index 00000000..84fbc517 --- /dev/null +++ b/apps/web/src/components/landing/SponsorItem.tsx @@ -0,0 +1,62 @@ +import Image from "next/image"; +import Link from "next/link"; +import { Partner } from "@/lib/utils/shared/types"; + +const capitalize = (input: string) => + input.charAt(0).toUpperCase() + input.slice(1); + +const tierColorMap = { + title: "text-[#E817C6]", + gold: "text-yellow-600", + silver: "text-gray-400", + bronze: "text-[#a97142]", + partner: "text-[#17C6E8]", +} as const; + +export function SponsorItem({ name, logo, tier, url }: Partner) { + const textColor = tierColorMap[tier]; + const isTitle = tier === "title"; + + return ( + +
+
+
+ {`${name}'s +
+
+
+

+ {name} +

+

+ {capitalize(tier)} Sponsor +

+
+
+ + ); +} diff --git a/apps/web/src/components/landing/Sponsors.tsx b/apps/web/src/components/landing/Sponsors.tsx new file mode 100644 index 00000000..d0a65545 --- /dev/null +++ b/apps/web/src/components/landing/Sponsors.tsx @@ -0,0 +1,48 @@ +import { SponsorItem } from "./SponsorItem"; +import { Partner } from "@/lib/utils/shared/types"; +import partners from "./partners.json"; + +export async function Sponsors() { + const partnerList: Partner[] = partners.partners as Partner[]; + + const titleSponsors = partnerList.filter((p) => p.tier === "title"); + const otherSponsors = partnerList.filter((p) => p.tier !== "title"); + + return ( +
+
+
+

+ a huge thanks to our rowdyhacks partners +

+
+ + {titleSponsors.length > 0 && ( +
+ {titleSponsors.map(({ name, url, logo, tier }) => ( + + ))} +
+ )} + +
+ {otherSponsors.map(({ name, url, logo, tier }) => ( + + ))} +
+
+
+ ); +} diff --git a/apps/web/src/components/landing/Team.tsx b/apps/web/src/components/landing/Team.tsx new file mode 100644 index 00000000..994e4ceb --- /dev/null +++ b/apps/web/src/components/landing/Team.tsx @@ -0,0 +1,83 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "../shadcn/ui/carousel"; +import { Oswald } from "next/font/google"; +import Autoplay from "embla-carousel-autoplay"; +import TeamCard from "@/components/landing/TeamCard"; +import axios from "axios"; + +const oswald = Oswald({ + variable: "--font-oswald", + subsets: ["latin"], +}); + +const CarouselDefault = () => { + const [team, setTeam] = useState([]); + const [loading, setLoading] = useState(true); + + const plugin = React.useRef( + Autoplay({ delay: 2500, stopOnInteraction: true }), + ); + + // LOAD TEAM + useEffect(() => { + setLoading(true); + axios.get("/team.json").then((res) => { + setTeam(res.data.team); + }); + setLoading(false); + }, []); + + if (loading || team === undefined) return
Loading...
; + if (team.length === 0) return
No team members found.
; + + return ( + <> + + + {team.map((p, index) => ( + + + + ))} + + + + + + ); +}; + +export default function Team() { + return ( +
+
+

+ Meet The Team That Made RowdyHacks X Possible! +

+
+
+ +
+
+ ); +} diff --git a/apps/web/src/components/landing/TeamCard.tsx b/apps/web/src/components/landing/TeamCard.tsx new file mode 100644 index 00000000..02bc9e0b --- /dev/null +++ b/apps/web/src/components/landing/TeamCard.tsx @@ -0,0 +1,258 @@ +import { Card } from "@/components/shadcn/ui/card"; +import Image from "next/image"; +import { Github, Instagram, Linkedin } from "lucide-react"; +import Link from "next/link"; + +type TeamCardProps = { + person: Person; +}; + +export default function TeamCard({ person }: TeamCardProps) { + let borderColor, mainColor, glowColor, textColor; + + switch (person.team) { + case "Director": + case "Co-Director": + borderColor = + "linear-gradient(to bottom right, #EA580C, #FDE047, #EA580C)"; + mainColor = "#1E40AF"; + glowColor = "#4664D7"; + textColor = "white"; + break; + case "Logistics / PR": + case "Logistics": + borderColor = + "linear-gradient(to bottom right, #629584, #243642, #629584)"; + mainColor = "#387478"; + glowColor = "#CBD9D0"; + textColor = "white"; + break; + case "Hacker Experience": + borderColor = + "linear-gradient(to bottom right, #7E60BF, #E4B1F0, #7E60BF)"; + mainColor = "#433878"; + glowColor = "#FFE1FF"; + textColor = "white"; + break; + case "Tech / Logistics": + case "Tech": + borderColor = + "linear-gradient(to bottom right, #A0153E, #5D0E41, #A0153E)"; + mainColor = "#00224D"; + glowColor = "#FF204E"; + textColor = "white"; + break; + case "Media": + borderColor = + "linear-gradient(to bottom right, #B17457, #D8D2C2, #B17457)"; + mainColor = "#4A4947"; + glowColor = "#FAF7F0"; + textColor = "white"; + break; + case "Public Relations": + borderColor = + "linear-gradient(to bottom right, #D3EE98, #A0D683, #D3EE98)"; + mainColor = "#72BF78"; + glowColor = "#F3C623"; + textColor = "white"; + break; + case "Design": + borderColor = + "linear-gradient(to bottom right, #FFB0B0, #FFD09B, #FFB0B0)"; + mainColor = "#CB80AB"; + glowColor = "#FFECC8"; + textColor = "white"; + break; + case "Outreach": + borderColor = + "linear-gradient(to bottom right, #161D6F, #0B2F9F, #161D6F)"; + mainColor = "#98DED9"; + glowColor = "#C7FFD8"; + textColor = "white"; + break; + } + + return ( + <> +
+
+
+ +
+
+ {"logo"} +

+ {person.name} +

+
+
+ {`${person.name} +
+

+ {person.team} +

+
+
+
+
+ +
+
+ {"logo"} +

+ {person.name} +

+
+
+ {`${person.name} +
+
+ {person.linkedin ? ( + + + + ) : null} + {person.github ? ( + + + + ) : null} + {person.personal ? ( + + + + ) : null} +
+
+
+
+
+
+ + ); +} diff --git a/apps/web/src/components/landing/TeamMember.tsx b/apps/web/src/components/landing/TeamMember.tsx deleted file mode 100644 index 63cfab4a..00000000 --- a/apps/web/src/components/landing/TeamMember.tsx +++ /dev/null @@ -1,146 +0,0 @@ -"use client"; - -import { Person } from "./Person"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "../shadcn/ui/card"; -import { Oswald } from "next/font/google"; -import Image from "next/image"; -import { useState } from "react"; -import c from "config"; -const oswald = Oswald({ - variable: "--font-oswald", - subsets: ["latin"], -}); - -// Using the raw svg tag is inefficient. Will need to change later -function LinkedIn({ fillColor }: { fillColor: string }) { - return ( - - - - ); -} - -function Website({ fillColor }: { fillColor: string }) { - return ( - - - - ); -} - -function Github({ fillColor }: { fillColor: string }) { - return ( - - - - ); -} - -export default function TeamMember({ person }: { person: Person }) { - // Edit the max width and height and then set the height to auto in the styling - - const [src, setSrc] = useState(person.imgLink); - const [styling, setStyling] = useState( - "max-w-[110px] md:max-w-[140px] lg:max-w-[160px] 2xl:max-w-[200px] h-auto rounded-lg", - ); - - const FallBackStyling = - "max-w-[105px] md:max-w-[132px] lg:max-w-[150px] xl:max-w-[151px] 2xl:max-w-[188px] rounded-lg"; - - return ( - -
- - -

{`${person.fname}\u00A0${person.lname}`}

-
- -

- {person.role} -

-
-
- - {/* This also needs to be fixed */} - Person Placeholder { - setSrc(`${c.icon.svg}`); - setStyling(FallBackStyling); - }} - /> - - - - -
-
- ); -} diff --git a/apps/web/src/components/landing/partners.json b/apps/web/src/components/landing/partners.json index fc654015..6eaa66f4 100644 --- a/apps/web/src/components/landing/partners.json +++ b/apps/web/src/components/landing/partners.json @@ -1,142 +1,64 @@ { "partners": [ - { - "name": "Cymanii", - "logo": "CyManII_Logo.svg", - "url": "https://cymanii.org/", - "tier": "Gold Sponsor" - }, - { - "name": "UTSA DS Dept.", - "logo": "UTSADS.svg", - "url": "https://sds.utsa.edu/", - "tier": "Gold Sponsor" - }, { "name": "Swivel", - "logo": "swivel_logo.svg", - "url": "https://www.getswivel.io/", - "tier": "Silver Sponsor" - }, - { - "name": "UTSA CS Dept.", - "logo": "UTSA_CS.svg", - "url": "https://sciences.utsa.edu/computer-science/", - "tier": "Silver Sponsor" - }, - { - "name": "Frost Bank", - "logo": "FrostBank.svg", - "url": "https://www.frostbank.com/", - "tier": "Silver Sponsor" - }, - { - "name": "Valero", - "logo": "ValeroLogo.svg", - "url": "https://www.valero.com/", - "tier": "Silver Sponsor" - }, - { - "name": "Google", - "logo": "Google_Icon.svg", - "url": "https://about.google/", - "tier": "Bronze Sponsor" - }, - { - "name": "Paycom", - "logo": "PaycomLogo.svg", - "url": "https://www.paycom.com/", - "tier": "Bronze Sponsor" + "logo": "/img/sponsors/SWIVEL Knockout-b.svg", + "tier": "title", + "url": "https://www.getswivel.io/" }, { - "name": "Dell", - "logo": "Dell_Tech_Logo.svg", - "url": "https://www.dell.com/", - "tier": "Bronze Sponsor" - }, - { - "name": "S + S", - "logo": "Students_and_Startups.svg", - "url": "https://studentsstartups.com/", - "tier": "Bronze Sponsor" - }, - { - "name": "AFCS", - "logo": "AFCSLogo.svg", - "url": "https://afciviliancareers.com/", - "tier": "Bronze Sponsor" - }, - { - "name": "Accenture", - "logo": "Accenture-logo.svg", - "url": "https://www.accenture.com/", - "tier": "Rowdy Partner" + "name": "Schroeder", + "logo": "/img/sponsors/SCHROEDER.png", + "tier": "bronze", + "url": "/" }, { - "name": "UTSA COE", - "logo": "UTSA_COE.svg", - "url": "https://klesse.utsa.edu/", - "tier": "Rowdy Partner" + "name": "Groq", + "logo": "/img/sponsors/GROQ.png", + "tier": "silver", + "url": "https://groq.com/" }, { - "name": "UTSA Tech Store", - "logo": "rowdy_tech_logo.svg", - "url": "https://campustechnologystore.com/campustechnologystore/", - "tier": "Rowdy Partner" + "name": "Matrix AI", + "logo": "/img/sponsors/MATRIX.png", + "tier": "partner", + "url": "https://ai.utsa.edu/" }, { - "name": "TD Synnex", - "logo": "TD_Synnex_logo.svg", - "url": "https://www.tdsynnex.com/", - "tier": "Rowdy Partner" + "name": "CyManii", + "logo": "/img/sponsors/CYMANII.svg", + "tier": "silver", + "url": "https://cymanii.org/" }, { - "name": "Wolfram Alpha", - "logo": "wolfram_logo.svg", - "url": "https://www.wolframalpha.com/", - "tier": "Rowdy Partner" + "name": "Klesse Engineering", + "logo": "/img/sponsors/KLESSE.svg", + "tier": "partner", + "url": "https://klesse.utsa.edu/" }, { - "name": "CodePath", - "logo": "Codepath_logo.svg", - "url": "https://www.codepath.org/", - "tier": "Rowdy Partner" + "name": "UTSA Data Science", + "logo": "/img/sponsors/UTSA_DATA_SCIENCE.svg", + "tier": "gold", + "url": "https://sds.utsa.edu/" }, { - "name": "ACM", - "logo": "ACM_logo.svg", - "url": "https://www.acm.org/", - "tier": "Rowdy Partner" + "name": "UTSA Computer Science", + "logo": "/img/sponsors/UTSA_CS.svg", + "tier": "silver", + "url": "https://sciences.utsa.edu/computer-science/" }, { - "name": "Artea", - "logo": "Artea_logo.svg", - "url": "https://www.drinkartea.com/", - "tier": "Rowdy In-Kind" + "name": "HEB", + "logo": "/img/sponsors/HEB.svg", + "tier": "silver", + "url": "https://www.heb.com/" }, { - "name": "Pho Thien An", - "logo": "Pho_logo.svg", - "url": "https://www.phothienan.com/", - "tier": "Rowdy In-Kind" - }, - { - "name": "Bunz Burgers", - "logo": "Bunz_logo.svg", - "url": "https://www.tastybunz.com/", - "tier": "Rowdy In-Kind" - }, - { - "name": "H-E-B", - "logo": "HEB.svg", - "url": "https://www.heb.com/", - "tier": "Rowdy In-Kind" - }, - { - "name": "Six Flags", - "logo": "Six_Flags_logo.svg", - "url": "https://www.sixflags.com/fiestatexas", - "tier": "Rowdy In-Kind" + "name": "Dell", + "logo": "/img/sponsors/DELL.svg", + "tier": "silver", + "url": "https://www.dell.com/en-us" } ] } diff --git a/apps/web/src/components/landing/team.json b/apps/web/src/components/landing/team.json new file mode 100644 index 00000000..13c718b0 --- /dev/null +++ b/apps/web/src/components/landing/team.json @@ -0,0 +1,374 @@ +{ + "team": [ + { + "fname": "Liam", + "lname": "Murray1", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray2", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray3", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray4", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray5", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray6", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray7", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray8", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray9", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray10", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray11", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray12", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray13", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray14", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray15", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray16", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray17", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray18", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray19", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray20", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray21", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray22", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray23", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray24", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray25", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray26", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray27", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray28", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray29", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray30", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray31", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray32", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray33", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray34", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray35", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray36", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + }, + { + "fname": "Liam", + "lname": "Murray37", + "photo": "", + "role": "Logistics Lead/Tech", + "linkedin": "https://www.linkedin.com/in/liamrmurray/", + "website": "https://www.liammurray.dev/", + "github": "https://github.com/Lermatroid", + "persona": "" + } + ] +} diff --git a/apps/web/src/components/registration/RegisterForm.tsx b/apps/web/src/components/registration/RegisterForm.tsx index a7d81759..1658a8ff 100644 --- a/apps/web/src/components/registration/RegisterForm.tsx +++ b/apps/web/src/components/registration/RegisterForm.tsx @@ -116,10 +116,6 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { } }, [universityValue]); - useEffect(() => { - console.log(countryValue); - }, [countryValue]); - async function onSubmit(data: z.infer) { console.log(data); setIsLoading(true); @@ -1147,11 +1143,19 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { >

- {uploadedFile - ? `${uploadedFile.name} (${Math.round(uploadedFile.size / 1024)}kb)` - : isDragActive - ? "Drop your resume here..." - : "Drag 'n' drop your resume here, or click to select a file"} + {uploadedFile ? ( + `${uploadedFile.name} (${Math.round(uploadedFile.size / 1024)}kb)` + ) : isDragActive ? ( + "Drop your resume here..." + ) : ( +

+ Drag 'n' drop your + resume here, or click to + select a file +
+ Accepted files: PDF +
+ )}

{uploadedFile ? (
+

+ Public Information +

+
+
+ +
+
+ @ +
+ { + setNewHackerTag(e.target.value); + setHackerTagTakenAlert(false); + }} + /> +
+ {hackerTagTakenAlert ? ( +
+ HackerTag is already taken! +
+ ) : ( + "" + )} + {!newHackerTag ? ( +
+ This field can't be empty! +
+ ) : null} +
+
- Update - + + setNewIsProfileSearchable( + !newIsProfileSearchable, + ) + } + /> + +
+
); diff --git a/apps/web/src/components/settings/DiscordVerifyButton.tsx b/apps/web/src/components/settings/DiscordVerifyButton.tsx index df30e051..5a70f7b8 100644 --- a/apps/web/src/components/settings/DiscordVerifyButton.tsx +++ b/apps/web/src/components/settings/DiscordVerifyButton.tsx @@ -1,7 +1,7 @@ "use client"; import { Button } from "@/components/shadcn/ui/button"; -import { useAction } from "next-safe-action/hook"; +import { useAction } from "next-safe-action/hooks"; import { confirmVerifyDiscord } from "@/actions/discord-verify"; import { useRouter, useSearchParams } from "next/navigation"; import { toast } from "sonner"; diff --git a/apps/web/src/components/settings/ProfileSettings.tsx b/apps/web/src/components/settings/ProfileSettings.tsx index 067c4071..8f455aaa 100644 --- a/apps/web/src/components/settings/ProfileSettings.tsx +++ b/apps/web/src/components/settings/ProfileSettings.tsx @@ -5,50 +5,80 @@ import { Button } from "@/components/shadcn/ui/button"; import { Label } from "@/components/shadcn/ui/label"; import { Textarea } from "@/components/shadcn/ui/textarea"; import { - modifyRegistrationData, + modifyProfileData, updateProfileImage, } from "@/actions/user-profile-mod"; import { useUser } from "@clerk/nextjs"; -import { useAction } from "next-safe-action/hook"; +import { useAction } from "next-safe-action/hooks"; import { toast } from "sonner"; import { useState } from "react"; import { encodeFileAsBase64 } from "@/lib/utils/shared/files"; +import { Tag, TagInput } from "@/components/shadcn/ui/tag/tag-input"; +import { Loader2 } from "lucide-react"; +import { Avatar, AvatarImage } from "@/components/shadcn/ui/avatar"; -interface ProfileSettingsProps { +interface ProfileData { + pronouns: string; bio: string; - university: string; + skills: string[]; + discord: string | null; + profilePhoto: string; } -export default function ProfileSettings({ - bio, - university, -}: ProfileSettingsProps) { - const [newBio, setNewBio] = useState(bio); - const [newUniversity, setNewUniversity] = useState(university); +interface ProfileSettingsProps { + profile: ProfileData; +} + +export default function ProfileSettings({ profile }: ProfileSettingsProps) { + const [newPronouns, setNewPronouns] = useState(profile.pronouns); + const [newBio, setNewBio] = useState(profile.bio); const [newProfileImage, setNewProfileImage] = useState(null); + let curSkills: Tag[] = []; + // for (let i = 0; i < profile.skills.length; i++) { + // let t: Tag = { + // id: profile.skills[i], + // text: profile.skills[i], + // }; + // curSkills.push(t); + // } + profile.skills.map((skill) => { + curSkills.push({ + id: skill, + text: skill, + }); + }); + const [newSkills, setNewSkills] = useState(curSkills); + const [newDiscord, setNewDiscord] = useState(profile.discord || ""); + + const [isProfilePictureLoading, setIsProfilePictureLoading] = + useState(false); + const [isProfileSettingsLoading, setIsProfileSettingsLoading] = + useState(false); + const { user } = useUser(); - const { execute: runModifyRegistrationData } = useAction( - modifyRegistrationData, - { - onSuccess: () => { - toast.dismiss(); - toast.success("Profile updated successfully!"); - }, - onError: () => { - toast.dismiss(); - toast.error("An error occurred while updating your profile!"); - }, + const { execute: runModifyProfileData } = useAction(modifyProfileData, { + onSuccess: () => { + setIsProfileSettingsLoading(false); + toast.dismiss(); + toast.success("Profile updated successfully!"); }, - ); + onError: () => { + setIsProfileSettingsLoading(false); + toast.dismiss(); + toast.error("An error occurred while updating your profile!"); + }, + }); const { execute: runUpdateProfileImage } = useAction(updateProfileImage, { onSuccess: async () => { + setIsProfilePictureLoading(false); toast.dismiss(); await user?.setProfileImage({ file: newProfileImage }); toast.success("Profile Photo updated successfully!"); }, onError: (err) => { + setIsProfilePictureLoading(false); toast.dismiss(); toast.error("An error occurred while updating your profile photo!"); console.error(err); @@ -66,7 +96,12 @@ export default function ProfileSettings({

Profile Photo

- + + +

Profile Data

-
- {/*
- - setNewUniversity(e.target.value)} - /> -
*/} +
+ + setNewPronouns(e.target.value)} + /> +
+
- +