diff --git a/.gitignore b/.gitignore index 6ea11cca..9f67afc6 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,8 @@ yarn-error.log* .turbo # vercel -.vercel \ No newline at end of file +.vercel +.env*.local + +# vscode +.vscode diff --git a/README.md b/README.md index a4738b20..4467153e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,24 @@ -

- Material Bread logo -

-

HackKit

-

Feature-packed Hackathon Managment Software


More Here Soon...

+# 🦕 RowdyHacks IX: _A Land Before RowdyHacks_ + +**The offical website for RowdyHacks 2024!** + +## Technologies Used + +- TypeScript +- Next.js +- Drizzle ORM +- Tailwind CSS + +[![Vercel](https://static.rowdyhacks.org/img/powered-by-vercel.svg)](https://vercel.com/?utm_source=ACM%20UTSA&utm_campaign=oss) + +## Join The Team + +Want to help make RowdyHacks the best Hackathon around? Please reach out to us at `team@rowdyhacks.org`! + +## RH 2024 Website 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 1af220b1..f142c00d 100644 --- a/apps/bot/bot.ts +++ b/apps/bot/bot.ts @@ -45,7 +45,7 @@ for (const file of commandFiles) { console.log( `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.` ); - } + } } console.log(`Loaded ${client.commands.size} Commands`); @@ -174,6 +174,7 @@ app.post("/api/checkDiscordVerification", async (h) => { } if (body.code === undefined || typeof body.code !== "string") { + console.log("failed cause of malformed body"); return h.json({ success: false }); } @@ -184,6 +185,7 @@ app.post("/api/checkDiscordVerification", async (h) => { console.log("got here 1 with verification ", verification); if (!verification || !verification.clerkID) { + console.log("failed cause of no verification or missing clerkID"); return h.json({ success: false }); } console.log("got here 2"); @@ -192,6 +194,7 @@ app.post("/api/checkDiscordVerification", async (h) => { }); console.log("got here 2 with user", user); if (!user) { + console.log("failed cause of no user in db"); return h.json({ success: false }); } @@ -201,6 +204,7 @@ app.post("/api/checkDiscordVerification", async (h) => { const guild = client.guilds.cache.get(verification.guild); if (!guild) { + console.log("failed cause of no guild on intereaction"); return h.json({ success: false }); } @@ -208,12 +212,19 @@ app.post("/api/checkDiscordVerification", async (h) => { const userGroupRole = guild.roles.cache.find((role) => role.name === userGroupRoleName); if (!role || !userGroupRole) { + console.log( + "failed cause could not find a role, was looking for group " + + user.group + + " called " + + userGroupRoleName + ); return h.json({ success: false }); } const member = guild.members.cache.get(verification.discordUserID); if (!member) { + console.log("failed cause could not find member"); return h.json({ success: false }); } diff --git a/apps/web/package.json b/apps/web/package.json index 4f91be9d..f5140a4d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -12,9 +12,11 @@ "dependencies": { "@aws-sdk/client-ses": "^3.485.0", "@clerk/nextjs": "^4.29.4", + "@gsap/react": "^2.0.2", "@hookform/resolvers": "^3.1.1", "@internationalized/date": "^3.3.0", "@planetscale/database": "^1.8.0", + "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-avatar": "^1.0.3", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4", @@ -30,6 +32,7 @@ "@types/node": "20.3.2", "@types/react": "18.2.14", "@types/react-dom": "18.2.6", + "@vercel/analytics": "^1.1.1", "@vercel/blob": "^0.10.0", "@vercel/kv": "^1.0.1", "@vercel/postgres": "^0.4.0", @@ -45,6 +48,11 @@ "db": "workspace:*", "dotenv": "^16.3.1", "drizzle-zod": "^0.5.1", + "embla-carousel": "8.0.0-rc22", + "embla-carousel-autoplay": "8.0.0-rc22", + "embla-carousel-react": "8.0.0-rc20", + "framer-motion": "^10.18.0", + "gsap": "^3.12.4", "lucide-react": "^0.317.0", "nanoid": "^4.0.2", "next": "14.0.4", @@ -54,10 +62,12 @@ "postgres": "^3.3.5", "react": "18.2.0", "react-aria": "^3.26.0", + "react-bootstrap": "^2.10.0", "react-confetti": "^6.1.0", "react-dom": "18.2.0", "react-dropzone": "^14.2.3", "react-email": "^1.10.0", + "react-fast-marquee": "^1.6.2", "react-hook-form": "7.44.3", "react-loader-spinner": "^6.1.6", "react-parallax-tilt": "^1.7.154", @@ -73,6 +83,7 @@ "tailwindcss-animate": "^1.0.6", "title-case": "^3.0.3", "typescript": "5.1.6", + "use-debounce": "^10.0.1", "usehooks-ts": "^2.14.0", "vaul": "^0.8.0", "zod": "^3.21.4" diff --git a/apps/web/public/img/dash/pass/bg.png b/apps/web/public/img/dash/pass/bg.png deleted file mode 100644 index 646062c6..00000000 Binary files a/apps/web/public/img/dash/pass/bg.png and /dev/null differ diff --git a/apps/web/public/img/logo/hackkit.ico b/apps/web/public/img/logo/hackkit.ico deleted file mode 100644 index 4246b4ca..00000000 Binary files a/apps/web/public/img/logo/hackkit.ico and /dev/null differ diff --git a/apps/web/public/img/logo/logo.png b/apps/web/public/img/logo/logo.png deleted file mode 100644 index 4faed2a1..00000000 Binary files a/apps/web/public/img/logo/logo.png and /dev/null differ diff --git a/apps/web/public/img/partner-logos/ACM_logo.svg b/apps/web/public/img/partner-logos/ACM_logo.svg new file mode 100644 index 00000000..6218bc9e --- /dev/null +++ b/apps/web/public/img/partner-logos/ACM_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/AFCSLogo.svg b/apps/web/public/img/partner-logos/AFCSLogo.svg new file mode 100644 index 00000000..e9bf0e1e --- /dev/null +++ b/apps/web/public/img/partner-logos/AFCSLogo.svg @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Accenture-logo.svg b/apps/web/public/img/partner-logos/Accenture-logo.svg new file mode 100644 index 00000000..51366cb8 --- /dev/null +++ b/apps/web/public/img/partner-logos/Accenture-logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Artea_logo.svg b/apps/web/public/img/partner-logos/Artea_logo.svg new file mode 100644 index 00000000..bb89f601 --- /dev/null +++ b/apps/web/public/img/partner-logos/Artea_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Bunz_logo.svg b/apps/web/public/img/partner-logos/Bunz_logo.svg new file mode 100644 index 00000000..fea2ca20 --- /dev/null +++ b/apps/web/public/img/partner-logos/Bunz_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Codepath_logo.svg b/apps/web/public/img/partner-logos/Codepath_logo.svg new file mode 100644 index 00000000..6510e561 --- /dev/null +++ b/apps/web/public/img/partner-logos/Codepath_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/CyManII_Logo.svg b/apps/web/public/img/partner-logos/CyManII_Logo.svg new file mode 100644 index 00000000..1f24551a --- /dev/null +++ b/apps/web/public/img/partner-logos/CyManII_Logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Dell_Tech_Logo.svg b/apps/web/public/img/partner-logos/Dell_Tech_Logo.svg new file mode 100644 index 00000000..8f88bb30 --- /dev/null +++ b/apps/web/public/img/partner-logos/Dell_Tech_Logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/FrostBank.svg b/apps/web/public/img/partner-logos/FrostBank.svg new file mode 100644 index 00000000..f50dfb8d --- /dev/null +++ b/apps/web/public/img/partner-logos/FrostBank.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Google_Icon.svg b/apps/web/public/img/partner-logos/Google_Icon.svg new file mode 100644 index 00000000..e2cae128 --- /dev/null +++ b/apps/web/public/img/partner-logos/Google_Icon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/HEB.svg b/apps/web/public/img/partner-logos/HEB.svg new file mode 100644 index 00000000..0a43d02b --- /dev/null +++ b/apps/web/public/img/partner-logos/HEB.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Marathon_Oil_Logo.svg b/apps/web/public/img/partner-logos/Marathon_Oil_Logo.svg new file mode 100644 index 00000000..a33f13fe --- /dev/null +++ b/apps/web/public/img/partner-logos/Marathon_Oil_Logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/PaycomLogo.svg b/apps/web/public/img/partner-logos/PaycomLogo.svg new file mode 100644 index 00000000..64fa54bc --- /dev/null +++ b/apps/web/public/img/partner-logos/PaycomLogo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Pho_logo.svg b/apps/web/public/img/partner-logos/Pho_logo.svg new file mode 100644 index 00000000..d4b901ab --- /dev/null +++ b/apps/web/public/img/partner-logos/Pho_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Six_Flags_logo.svg b/apps/web/public/img/partner-logos/Six_Flags_logo.svg new file mode 100644 index 00000000..b0712ed2 --- /dev/null +++ b/apps/web/public/img/partner-logos/Six_Flags_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/Students_and_Startups.svg b/apps/web/public/img/partner-logos/Students_and_Startups.svg new file mode 100644 index 00000000..be494750 --- /dev/null +++ b/apps/web/public/img/partner-logos/Students_and_Startups.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/img/partner-logos/TD_Synnex_logo.svg b/apps/web/public/img/partner-logos/TD_Synnex_logo.svg new file mode 100644 index 00000000..e483e73b --- /dev/null +++ b/apps/web/public/img/partner-logos/TD_Synnex_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/UTSADS.svg b/apps/web/public/img/partner-logos/UTSADS.svg new file mode 100644 index 00000000..531e83f5 --- /dev/null +++ b/apps/web/public/img/partner-logos/UTSADS.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/UTSA_COE.svg b/apps/web/public/img/partner-logos/UTSA_COE.svg new file mode 100644 index 00000000..10b5ec9d --- /dev/null +++ b/apps/web/public/img/partner-logos/UTSA_COE.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/UTSA_COS_CS.svg b/apps/web/public/img/partner-logos/UTSA_COS_CS.svg new file mode 100644 index 00000000..bca29198 --- /dev/null +++ b/apps/web/public/img/partner-logos/UTSA_COS_CS.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/UTSA_CS.svg b/apps/web/public/img/partner-logos/UTSA_CS.svg new file mode 100644 index 00000000..9733fe6f --- /dev/null +++ b/apps/web/public/img/partner-logos/UTSA_CS.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/ValeroLogo.svg b/apps/web/public/img/partner-logos/ValeroLogo.svg new file mode 100644 index 00000000..0a909f2a --- /dev/null +++ b/apps/web/public/img/partner-logos/ValeroLogo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/marathon_logo.svg b/apps/web/public/img/partner-logos/marathon_logo.svg new file mode 100644 index 00000000..5d93f342 --- /dev/null +++ b/apps/web/public/img/partner-logos/marathon_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/rowdy_tech_logo.svg b/apps/web/public/img/partner-logos/rowdy_tech_logo.svg new file mode 100644 index 00000000..33bad52c --- /dev/null +++ b/apps/web/public/img/partner-logos/rowdy_tech_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/swivel_logo.svg b/apps/web/public/img/partner-logos/swivel_logo.svg new file mode 100644 index 00000000..f003f366 --- /dev/null +++ b/apps/web/public/img/partner-logos/swivel_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/public/img/partner-logos/wolfram_logo.svg b/apps/web/public/img/partner-logos/wolfram_logo.svg new file mode 100644 index 00000000..5a4d600e --- /dev/null +++ b/apps/web/public/img/partner-logos/wolfram_logo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/web/src/actions/admin/user-actions.ts b/apps/web/src/actions/admin/user-actions.ts index f4d94d2b..caed729a 100644 --- a/apps/web/src/actions/admin/user-actions.ts +++ b/apps/web/src/actions/admin/user-actions.ts @@ -9,16 +9,22 @@ 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(users).set({ role: roleToSet }).where(eq(users.clerkID, userIDToUpdate)); - revalidatePath(`/admin/users/${userIDToUpdate}`); - return { success: true }; - } + 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(users) + .set({ role: roleToSet }) + .where(eq(users.clerkID, userIDToUpdate)); + revalidatePath(`/admin/users/${userIDToUpdate}`); + return { success: true }; + } ); diff --git a/apps/web/src/actions/user-profile-mod.ts b/apps/web/src/actions/user-profile-mod.ts index 0bd7b21e..682bcf51 100644 --- a/apps/web/src/actions/user-profile-mod.ts +++ b/apps/web/src/actions/user-profile-mod.ts @@ -3,36 +3,64 @@ import { authenticatedAction } from "@/lib/safe-action"; import { z } from "zod"; import { db } from "db"; -import { users, profileData } from "db/schema"; +import { users, profileData, registrationData } from "db/schema"; import { eq } from "db/drizzle"; import { put } from "@vercel/blob"; import { decodeBase64AsFile } from "@/lib/utils/shared/files"; import { revalidatePath } from "next/cache"; // TODO: Add skill updating -export const modifyUserBioAndSkills = authenticatedAction( - z.object({ bio: z.string().max(500), skills: z.string().max(100) }), - async ({ bio, skills }, { userId }) => { - const user = await db.query.users.findFirst({ where: eq(users.clerkID, userId) }); - if (!user) throw new Error("User not found"); - await db.update(profileData).set({ bio }).where(eq(profileData.hackerTag, user.hackerTag)); - return { success: true, newbio: bio }; - } +export const modifyRegistrationData = authenticatedAction( + z.object({ + bio: z.string().max(500), + skills: z.string().max(100), + }), + async ({ bio, skills }, { userId }) => { + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), + }); + if (!user) throw new Error("User not found"); + await db + .update(profileData) + .set({ bio }) + .where(eq(profileData.hackerTag, user.hackerTag)); + 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 db.query.users.findFirst({ + where: eq(users.clerkID, userId), + }); + if (!user) throw new Error("User not found"); + await db + .update(users) + .set({ firstName, lastName }) + .where(eq(users.clerkID, userId)); + return { success: true, newFirstName: firstName, newLastName: lastName }; + } ); 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 db.query.users.findFirst({ where: eq(users.clerkID, userId) }); - if (!user) throw new Error("User not found"); + z.object({ fileBase64: z.string(), fileName: z.string() }), + async ({ fileBase64, fileName }, { userId }) => { + const image = await decodeBase64AsFile(fileBase64, fileName); + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), + }); + if (!user) throw new Error("User not found"); - const blobUpload = await put(image.name, image, { access: "public" }); - await db - .update(profileData) - .set({ profilePhoto: blobUpload.url }) - .where(eq(profileData.hackerTag, user.hackerTag)); - revalidatePath("/settings/profile"); - return { success: true }; - } + const blobUpload = await put(image.name, image, { access: "public" }); + await db + .update(profileData) + .set({ profilePhoto: blobUpload.url }) + .where(eq(profileData.hackerTag, user.hackerTag)); + 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 c97d22c1..3d01b6f4 100644 --- a/apps/web/src/app/admin/check-in/page.tsx +++ b/apps/web/src/app/admin/check-in/page.tsx @@ -1,4 +1,4 @@ -import CheckinScanner from "@/components/dash/admin/scanner/CheckinScanner"; +import CheckinScanner from "@/components/admin/scanner/CheckinScanner"; import FullScreenMessage from "@/components/shared/FullScreenMessage"; import { db } from "db"; import { eq, and } from "db/drizzle"; @@ -33,38 +33,42 @@ export default async function Page({ // ); // } + // Returns only if search params exist if (searchParams.user) { - const [isChecked, scanUser] = await db.transaction(async (tx) => { + const [isChecked, scanUser,hasRSVPed] = await db.transaction(async (tx) => { const scanUser = await tx.query.users.findFirst({ where: eq(users.clerkID, searchParams.user ?? "unknown"), }); if (!scanUser) { - return [null, null]; + return [null, null,null]; } const scan = await tx - .select({ isChecked: users.checkedIn }) + .select({ isChecked: users.checkedIn, hasRSVPed:users.rsvp }) .from(users) .where(eq(users.clerkID, searchParams.user!)); if (scan) { - return [scan[0].isChecked, scanUser]; + return [scan[0].isChecked, scanUser,scan[0].hasRSVPed]; } else { - return [null, scanUser]; + return [null, scanUser,null]; } }); + return (
); } + // Fall through case return (
- +
); } diff --git a/apps/web/src/app/admin/events/new/page.tsx b/apps/web/src/app/admin/events/new/page.tsx index 319c035e..1170cbc6 100644 --- a/apps/web/src/app/admin/events/new/page.tsx +++ b/apps/web/src/app/admin/events/new/page.tsx @@ -1,10 +1,10 @@ -import NewEventForm from "@/components/dash/admin/events/NewEventForm"; +import NewEventForm from "@/components/admin/events/NewEventForm"; export default function Page() { const defaultDate = new Date(); return ( -
+

New Event

diff --git a/apps/web/src/app/admin/events/page.tsx b/apps/web/src/app/admin/events/page.tsx index 65f21a12..6f343beb 100644 --- a/apps/web/src/app/admin/events/page.tsx +++ b/apps/web/src/app/admin/events/page.tsx @@ -1,6 +1,6 @@ import { db } from "db"; -import { DataTable } from "@/components/dash/admin/events/EventDataTable"; -import { columns } from "@/components/dash/admin/events/EventColumns"; +import { DataTable } from "@/components/admin/events/EventDataTable"; +import { columns } from "@/components/admin/events/EventColumns"; import { Button } from "@/components/shadcn/ui/button"; import { PlusCircle } from "lucide-react"; import Link from "next/link"; diff --git a/apps/web/src/app/admin/layout.tsx b/apps/web/src/app/admin/layout.tsx index 63bfc7d2..ec77dea6 100644 --- a/apps/web/src/app/admin/layout.tsx +++ b/apps/web/src/app/admin/layout.tsx @@ -8,10 +8,12 @@ import DashNavItem from "@/components/dash/shared/DashNavItem"; import { eq } from "db/drizzle"; import { users } from "db/schema"; import FullScreenMessage from "@/components/shared/FullScreenMessage"; -import ProfileButton from "@/components/dash/shared/ProfileButton"; +import ProfileButton from "@/components/shared/ProfileButton"; import { Suspense } from "react"; import ClientToast from "@/components/shared/ClientToast"; import { redirect } from "next/navigation"; +import NavBarLinksGrouper from "@/components/shared/NavBarLinksGrouper"; + interface AdminLayoutProps { children: React.ReactNode; @@ -58,7 +60,7 @@ export default async function AdminLayout({ children }: AdminLayoutProps) { Home - + diff --git a/apps/web/src/app/admin/page.tsx b/apps/web/src/app/admin/page.tsx index aa35a384..76f2a223 100644 --- a/apps/web/src/app/admin/page.tsx +++ b/apps/web/src/app/admin/page.tsx @@ -1,4 +1,4 @@ -import { Overview } from "@/components/dash/admin/landing/Overview"; +import { Overview } from "@/components/admin/landing/Overview"; import { Card, CardHeader, @@ -9,7 +9,7 @@ import { import { db } from "db"; import { eq, desc } from "db/drizzle"; import { users } from "db/schema"; -import { Users, UserCheck, User2, TimerReset } from "lucide-react"; +import { Users, UserCheck, User2, TimerReset,MailCheck } from "lucide-react"; import type { userType } from "@/lib/utils/shared/types"; import { unstable_cache } from "next/cache"; import { env } from "@/env.mjs"; @@ -68,7 +68,7 @@ export default async function Page() { RSVPs - +
{rsvpCount}
@@ -78,7 +78,7 @@ export default async function Page() { Check-ins - +
{checkinCount}
@@ -132,19 +132,18 @@ function getRecentRegistrationData(users: userType[]) { // Format the date as YYYY-MM-DD const dateString = date.toISOString().split("T")[0]; - console.log("generated stamp: ", dateString); + // Assign a default value, e.g., 0 recentSignupCount[dateString] = 0; } for (const user of users) { - console.log(user); + if (user.rsvp) rsvpCount++; if (user.checkedIn) checkinCount++; const stamp = user.createdAt.toISOString().split("T")[0]; - console.log("user stamp: ", stamp); if (recentSignupCount[stamp] != undefined) recentSignupCount[stamp]++; } diff --git a/apps/web/src/app/admin/scanner/[id]/page.tsx b/apps/web/src/app/admin/scanner/[id]/page.tsx index 5eef2528..f7de2aa7 100644 --- a/apps/web/src/app/admin/scanner/[id]/page.tsx +++ b/apps/web/src/app/admin/scanner/[id]/page.tsx @@ -1,4 +1,4 @@ -import PassScanner from "@/components/dash/admin/scanner/PassScanner"; +import PassScanner from "@/components/admin/scanner/PassScanner"; import FullScreenMessage from "@/components/shared/FullScreenMessage"; import { db } from "db"; import { eq, and } from "db/drizzle"; diff --git a/apps/web/src/app/admin/toggles/landing/page.tsx b/apps/web/src/app/admin/toggles/landing/page.tsx index 24b0a9e3..642d19fe 100644 --- a/apps/web/src/app/admin/toggles/landing/page.tsx +++ b/apps/web/src/app/admin/toggles/landing/page.tsx @@ -1,4 +1,4 @@ -import { NavItemsManager, NavItemDialog } from "@/components/dash/admin/toggles/NavItemsManager"; +import { NavItemsManager, NavItemDialog } from "@/components/admin/toggles/NavItemsManager"; import { getAllNavItems } from "@/lib/utils/server/redis"; export default async function Page() { diff --git a/apps/web/src/app/admin/toggles/layout.tsx b/apps/web/src/app/admin/toggles/layout.tsx index 64a06a0f..eebd04f1 100644 --- a/apps/web/src/app/admin/toggles/layout.tsx +++ b/apps/web/src/app/admin/toggles/layout.tsx @@ -1,4 +1,4 @@ -import ToggleItem from "@/components/dash/admin/toggles/ToggleItem"; +import ToggleItem from "@/components/admin/toggles/ToggleItem"; interface ToggleLayoutProps { children: React.ReactNode; diff --git a/apps/web/src/app/admin/toggles/registration/page.tsx b/apps/web/src/app/admin/toggles/registration/page.tsx index 95088c1a..f1d9faa4 100644 --- a/apps/web/src/app/admin/toggles/registration/page.tsx +++ b/apps/web/src/app/admin/toggles/registration/page.tsx @@ -1,4 +1,4 @@ -import { RegistrationToggles } from "@/components/dash/admin/toggles/RegistrationSettings"; +import { RegistrationToggles } from "@/components/admin/toggles/RegistrationSettings"; import { kv } from "@vercel/kv"; import { parseRedisBoolean } from "@/lib/utils/server/redis"; diff --git a/apps/web/src/app/admin/users/[slug]/page.tsx b/apps/web/src/app/admin/users/[slug]/page.tsx index 3dced91a..c28b6699 100644 --- a/apps/web/src/app/admin/users/[slug]/page.tsx +++ b/apps/web/src/app/admin/users/[slug]/page.tsx @@ -7,13 +7,13 @@ import { Badge } from "@/components/shadcn/ui/badge"; import { Info } from "lucide-react"; import Link from "next/link"; -import UpdateRoleDialog from "@/components/dash/admin/users/UpdateRoleDialog"; +import UpdateRoleDialog from "@/components/admin/users/UpdateRoleDialog"; import { AccountInfo, PersonalInfo, ProfileInfo, TeamInfo, -} from "@/components/dash/admin/users/ServerSections"; +} from "@/components/admin/users/ServerSections"; import { auth } from "@clerk/nextjs"; import { notFound } from "next/navigation"; import { isUserAdmin } from "@/lib/utils/server/admin"; diff --git a/apps/web/src/app/admin/users/page.tsx b/apps/web/src/app/admin/users/page.tsx index 1272f72d..039d717f 100644 --- a/apps/web/src/app/admin/users/page.tsx +++ b/apps/web/src/app/admin/users/page.tsx @@ -1,37 +1,85 @@ -import { db } from "db"; -import { DataTable } from "@/components/dash/admin/users/UserDataTable"; -import { columns } from "@/components/dash/admin/users/UserColumns"; +import { db,ilike,or,and,eq } from "db"; +import { DataTable } from "@/components/admin/users/UserDataTable"; +import { columns } from "@/components/admin/users/UserColumns"; import { Button } from "@/components/shadcn/ui/button"; import { FolderInput } from "lucide-react"; +import { DefaultPagination } from "@/components/admin/users/DefaultPagination"; +import SearchBar from "@/components/admin/users/SearchBar"; +import Filters from "../../../components/admin/users/Filters"; +import { users } from "db/schema"; +import { parseCheckBoxParams } from "@/lib/utils/shared/pageParams"; -export default async function Page() { - const users = await db.query.users.findMany({ - with: { - registrationData: true, - profileData: true, - }, - }); - - return ( -
-
-
-
-

Users

-

{users.length} Total Users

-
-
- -
- {/* TODO: Would very much like to not have "as any" here in the future */} - -
- ); +export default async function Page({searchParams}:{searchParams:{[key:string]:string |undefined}}) { + // COME BACK AND CHANGE + const maxPerPage = 30; + + let page = +(searchParams["page"] ?? "1"); + let user = searchParams["user"] ?? ""; + const checkedBoxes = searchParams["checkedBoxes"] ?? ""; + + + console.log(checkedBoxes); + + const start = maxPerPage * (page - 1); + const end = maxPerPage + start; + + +// Might want to work with cache in prod to see if this will be plausible to do + const userData = await db.query.users.findMany({ + with: { + registrationData: true, + profileData: true, + }, + where: and( + or( + ilike(users.firstName, `%${user}%`), + ilike(users.lastName, `%${user}%`) + ), + ), + }); + + + return ( +
+
+
+
+

Users

+

+ Total Users: {userData.length} +

+
+
+ + +
+ {/* TODO: Would very much like to not have "as any" here in the future */} +
+ {userData && userData.length > 0 ? ( + <> + + + ) : ( +
+

No Results :(

+
+ )} + {/* */} +
+ +
+ ); } + +export const runtime = "edge"; +export const revalidate = 10; diff --git a/apps/web/src/app/contact/page.tsx b/apps/web/src/app/contact/page.tsx new file mode 100644 index 00000000..38c14a8a --- /dev/null +++ b/apps/web/src/app/contact/page.tsx @@ -0,0 +1,69 @@ +import Navbar from "@/components/shared/Navbar"; +import { Oswald } from "next/font/google"; +import c from "config"; +import { Instagram, Facebook, Twitter, Github } from "lucide-react"; +import Link from "next/link"; + +const oswald = Oswald({ + variable: "--font-oswald", + subsets: ["latin"], +}); + +export default function Page() { + return ( + <> + +
+
+
+

Contact Us

+

+ Have a question and want to reach out? Feel free to use one of the contact methods + listed here! +

+
+ + + + + + + + +
+ + + + + + + + + + + + +
+
+
+
+ + ); +} diff --git a/apps/web/src/app/dash/layout.tsx b/apps/web/src/app/dash/layout.tsx index e9352628..59e030a6 100644 --- a/apps/web/src/app/dash/layout.tsx +++ b/apps/web/src/app/dash/layout.tsx @@ -6,7 +6,7 @@ import Link from "next/link"; import { Button } from "@/components/shadcn/ui/button"; import DashNavItem from "@/components/dash/shared/DashNavItem"; import { redirect } from "next/navigation"; -import ProfileButton from "@/components/dash/shared/ProfileButton"; +import ProfileButton from "@/components/shared/ProfileButton"; import ClientToast from "@/components/shared/ClientToast"; interface DashLayoutProps { @@ -35,12 +35,12 @@ export default async function DashLayout({ children }: DashLayoutProps) { Home - + - + diff --git a/apps/web/src/app/dash/pass/page.tsx b/apps/web/src/app/dash/pass/page.tsx index 4afab8d2..4b558591 100644 --- a/apps/web/src/app/dash/pass/page.tsx +++ b/apps/web/src/app/dash/pass/page.tsx @@ -9,11 +9,23 @@ import c from "config"; import { format } from "date-fns"; import TiltWrapper from "@/components/dash/shared/TiltWrapper"; import { createQRpayload } from "@/lib/utils/shared/qr"; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "@/components/shadcn/ui/drawer"; + interface EventPassProps { user: InferModel; clerk: NonNullable>>; qrPayload: string; + guild:string; } export default async function Page() { @@ -27,68 +39,95 @@ export default async function Page() { if (!userDbRecord) return null; const qrPayload = createQRpayload({ userID: user.id, createdAt: new Date() }); + const guild = Object.keys(c.groups)[userDbRecord.group]; return (
- {/* */} - +
); } -function EventPass({ qrPayload, user, clerk }: EventPassProps) { +function EventPass({ qrPayload, user, clerk,guild }: EventPassProps) { return ( -
-
-
-
- {`${user.firstName}'s -

{user.firstName}

-

@{user.hackerTag}

-
-
-
- {""} -
-
- {`${c.hackathonName} -

- {c.hackathonName} {c.itteration} -

-
-
-

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

-

{c.prettyLocation}

-
-
-
-
-
- -
-
-
-
-
- ); +
+
+
+
+ {`${user.firstName}'s +

+ {user.firstName} +

+
+

@{user.hackerTag}

+

{guild}

+
+
+
+
+ {""} +
+
+ {`${c.hackathonName} +

+ {c.hackathonName}{" "} + {c.itteration} +

+
+
+

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

+

{c.prettyLocation}

+
+
+
+
+ + +
+ +
+
+ + + +
+
+

Psst! Click To Enlarge QR Code

+
+
+
+ ); } export const runtime = "edge"; diff --git a/apps/web/src/app/dash/schedule/page.tsx b/apps/web/src/app/dash/schedule/page.tsx index cf373be5..867470b0 100644 --- a/apps/web/src/app/dash/schedule/page.tsx +++ b/apps/web/src/app/dash/schedule/page.tsx @@ -4,51 +4,21 @@ import { db } from "db"; import { format, compareAsc } from "date-fns"; import { type ReactNode } from "react"; import { formatInTimeZone } from "date-fns-tz"; +import { redirect } from 'next/navigation'; export default async function Page() { + const events = await db.query.events.findMany(); - let days: Record = {}; - let toRender: ReactNode[] = []; - events.forEach((event) => { - const key = format(event.startTime, "MM/dd"); - if (!days[key]) { - days[key] = [event]; - } else { - days[key].push(event); - } - }); - const entries = Object.entries(days); - entries.sort((a, b) => { - const dateA = a[1][0].startTime; - const dateB = b[1][0].startTime; - return compareAsc(dateA, dateB); - }); + return ( -
-
-

{c.hackathonName}

-

- Schedule -

- {/*
-
-
-
*/} -
- {/* - */} - {entries.map(([key, value]) => ( - - ))} -
-
- ); +
+

+ Bug with Scheduling was found. Fix Coming soon! +

+

- Christian

+
+ ); } export const runtime = "edge"; diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index 645edc25..2d8a8675 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -4,13 +4,13 @@ @layer base { :root { - --background: 0 0% 100%; + --background: 32 48% 94%; --foreground: 240 10% 3.9%; - --nav: 255, 255, 255; - --hackathon-primary: 206 86% 52%; + --nav: 32 48% 94%; + --hackathon-primary: 67 42% 62%; - --muted: 240 4.8% 95.9%; + --muted: 28 27% 53%; --muted-foreground: 240 3.8% 46.1%; --popover: 0 0% 100%; @@ -38,17 +38,18 @@ --radius: 0.5rem; - --gradient-color-1: #668cff; - --gradient-color-2: #3366ff; - --gradient-color-3: #002db3; - --gradient-color-4: #1952cc; - } + --gradient-color-1: #355c2b; + --gradient-color-2: #94a33d; + --gradient-color-3: #babc34; + --gradient-color-4: #b5bf63; + + } .dark { --background: 240 10% 3.9%; --foreground: 0 0% 98%; - --nav: 0, 0, 0; + --nav: 0 0% 0%; --muted: 240 3.7% 15.9%; --muted-foreground: 240 5% 64.9%; @@ -78,6 +79,7 @@ } } + @layer base { * { @apply border-border; @@ -86,6 +88,26 @@ @apply bg-background text-foreground; } } +@layer utilities{ + .arrow_animate { + animation: arrow 2s infinite; + } +} +@keyframes arrow { + 0% { + + transform: translate(0px); + } + + 50% { + transform: translate(20px); + } + + 100% { + transform: translate(0px); + } + +} .no-select { user-drag: none; @@ -104,3 +126,33 @@ .event-pass-img { transform: translateZ(40px); } + +/* Chrome, Safari and Opera */ +.no-scrollbar::-webkit-scrollbar { + display: none; +} + +.no-scrollbar { + -ms-overflow-style: none; + /* IE and Edge */ + scrollbar-width: none; + /* Firefox */ +} +.arrow_animate { + animation: arrow 2s infinite; +} + +@keyframes arrow { + 0% { + + transform: translate(0px); + } + + 50% { + transform: translate(20px); + } + + 100% { + transform: translate(0px); + } +} diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 3a18aa29..7276068c 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,6 +1,7 @@ import "./globals.css"; import { ClerkProvider } from "@clerk/nextjs"; import { cookies } from "next/headers"; +import { Analytics } from "@vercel/analytics/react"; import { defaultTheme } from "config"; export default function RootLayout({ children }: { children: React.ReactNode }) { @@ -8,7 +9,10 @@ export default function RootLayout({ children }: { children: React.ReactNode }) return ( - {children} + + {children} + + ); diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 18594dfb..1ac1956d 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -1,68 +1,38 @@ -import Image from "next/image"; -import c from "config"; -import { Button } from "@/components/shadcn/ui/button"; -import Link from "next/link"; import Navbar from "@/components/shared/Navbar"; +import Hero from "@/components/landing/Hero"; + +import About from "@/components/landing/About"; + +import Partners from "@/components/landing/Partners"; +import Team from "@/components/landing/Team"; +import Footer from "@/components/landing/Footer"; +import MLHBadge from "@/components/landing/MLHBadge"; + +import { Oswald } from "next/font/google"; +import WorkWithUs from "@/components/landing/WorkWithUs"; + +const oswald = Oswald({ + variable: "--font-oswald", + subsets: ["latin"], +}); export default function Home() { return ( - <> +
-
-
-
-
-
-
-
- HackKit Logo -
-
-

- Hack -
- Kit -

-
-
-

- Feature-packed Hackathon managment software that just works. -

-
-
-
- - - - - - - - - -
-
- - Powered by Vercel - -
-
+ +
+ + + + + + +
- +
); } + +export const runtime = "edge"; +export const revalidate = "30"; diff --git a/apps/web/src/app/register/page.tsx b/apps/web/src/app/register/page.tsx index e20d9a49..e5ed350b 100644 --- a/apps/web/src/app/register/page.tsx +++ b/apps/web/src/app/register/page.tsx @@ -63,11 +63,12 @@ export default async function Page() {

{c.hackathonName}

+ {/* Why is this not a component? This same code is in here and insideo of sign-up */}

Registration

-
-

Registration Is Currently Closed

+
+

Registration Is Currently Closed

If you believe this is a mistake or have any questions, feel free to reach out to us at{" "} {c.issueEmail}! @@ -76,9 +77,9 @@ export default async function Page() { -

+

Already registered? - + Sign-in.

diff --git a/apps/web/src/app/settings/account/page.tsx b/apps/web/src/app/settings/account/page.tsx index dc9b46a5..3602f6e0 100644 --- a/apps/web/src/app/settings/account/page.tsx +++ b/apps/web/src/app/settings/account/page.tsx @@ -1,5 +1,18 @@ import AccountSettings from "@/components/settings/AccountSettings"; +import { users } from "db/schema"; +import { eq } from "db/drizzle"; +import { auth } from "@clerk/nextjs"; +import { db } from "db"; +import { redirect } from "next/navigation"; -export default function Page() { - return ; +export default async function Page() { + const { userId } = auth(); + const user = await db.query.users.findFirst({ + with: { registrationData: true }, + where: eq(users.clerkID, userId!), + }); + 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 8ca128b8..248f704c 100644 --- a/apps/web/src/app/settings/layout.tsx +++ b/apps/web/src/app/settings/layout.tsx @@ -1,4 +1,4 @@ -import { auth } from "@clerk/nextjs"; +import { auth, currentUser } from "@clerk/nextjs"; import { redirect } from "next/navigation"; import type { ReactNode } from "react"; import SettingsSection from "@/components/settings/SettingsSection"; @@ -7,36 +7,39 @@ import { Settings } from "lucide-react"; import ClientToast from "@/components/shared/ClientToast"; export default async function ({ children }: { children: ReactNode }) { - const { userId, user } = await auth(); + const { userId } = await auth(); + const user = await currentUser(); - if (!user || !userId) return redirect("/sign-in"); + if (!user || !userId) { + return redirect("/sign-in"); + } - if (!user.publicMetadata.registrationComplete) { - return redirect("/register"); - } + if (!user.publicMetadata.registrationComplete) { + return redirect("/register"); + } - return ( - <> - - -
-
-
-
-

- - Settings -

-
-
-
-
- - - -
-
{children}
-
- - ); + return ( + <> + + +
+
+
+
+

+ + Settings +

+
+
+
+
+ {/* */} + + +
+
{children}
+
+ + ); } diff --git a/apps/web/src/app/settings/profile/page.tsx b/apps/web/src/app/settings/profile/page.tsx index 65e8ba0a..3e243c50 100644 --- a/apps/web/src/app/settings/profile/page.tsx +++ b/apps/web/src/app/settings/profile/page.tsx @@ -5,16 +5,22 @@ import { eq } from "db/drizzle"; import { auth } from "@clerk/nextjs"; export default async function Page() { - const { userId } = auth(); - if (!userId) throw new Error("User not found"); - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - with: { - profileData: true, - }, - }); - if (!user) throw new Error("User not found"); - return ; + const { userId } = auth(); + if (!userId) throw new Error("User not found"); + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), + with: { + profileData: true, + registrationData: true, + }, + }); + if (!user) throw new Error("User not found"); + return ( + + ); } export const runtime = "edge"; diff --git a/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx b/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx index 87a3245a..7be35932 100644 --- a/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx +++ b/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx @@ -28,7 +28,7 @@ export default async function Page() { Registration
-

Registration Is Currently Closed

+

Registration Is Currently Closed

If you believe this is a mistake or have any questions, feel free to reach out to us at{" "} {c.issueEmail}! @@ -39,7 +39,7 @@ export default async function Page() {

Already registered?{" "} - + Sign-in.

diff --git a/apps/web/src/components/dash/admin/events/EventColumns.tsx b/apps/web/src/components/admin/events/EventColumns.tsx similarity index 100% rename from apps/web/src/components/dash/admin/events/EventColumns.tsx rename to apps/web/src/components/admin/events/EventColumns.tsx diff --git a/apps/web/src/components/dash/admin/events/EventDataTable.tsx b/apps/web/src/components/admin/events/EventDataTable.tsx similarity index 100% rename from apps/web/src/components/dash/admin/events/EventDataTable.tsx rename to apps/web/src/components/admin/events/EventDataTable.tsx diff --git a/apps/web/src/components/dash/admin/events/NewEventForm.tsx b/apps/web/src/components/admin/events/NewEventForm.tsx similarity index 100% rename from apps/web/src/components/dash/admin/events/NewEventForm.tsx rename to apps/web/src/components/admin/events/NewEventForm.tsx diff --git a/apps/web/src/components/dash/admin/landing/Overview.tsx b/apps/web/src/components/admin/landing/Overview.tsx similarity index 100% rename from apps/web/src/components/dash/admin/landing/Overview.tsx rename to apps/web/src/components/admin/landing/Overview.tsx diff --git a/apps/web/src/components/admin/scanner/CheckinScanner.tsx b/apps/web/src/components/admin/scanner/CheckinScanner.tsx new file mode 100644 index 00000000..b21542a4 --- /dev/null +++ b/apps/web/src/components/admin/scanner/CheckinScanner.tsx @@ -0,0 +1,198 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { QrScanner } from "@yudiel/react-qr-scanner"; +import superjson from "superjson"; +import { getScan, checkInUser } from "@/actions/admin/scanner-admin-actions"; +import { useAction, useOptimisticAction } from "next-safe-action/hook"; +import { type QRDataInterface } from "@/lib/utils/shared/qr"; +import type { scansType, userType, eventType } from "@/lib/utils/shared/types"; + +import { + Drawer, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "@/components/shadcn/ui/drawer"; +import { Button } from "@/components/shadcn/ui/button"; +import Link from "next/link"; +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. + +*/ + +interface CheckinScannerProps { + // event: eventType; + hasScanned: boolean; + // scan: scansType | null; + checkedIn: boolean | null; + scanUser: userType | null; + hasRSVP: boolean | null; +} + +export default function CheckinScanner({ + // event, + hasScanned, + checkedIn, + scanUser, + hasRSVP +}: CheckinScannerProps) { + const [scanLoading, setScanLoading] = useState(false); + const { execute: runScanAction } = useAction(checkInUser, {}); + const [proceed,setProceed] = useState(hasRSVP); + useEffect(() => { + if (hasScanned) { + setScanLoading(false); + } + }, [hasScanned]); + + const searchParams = useSearchParams(); + const path = usePathname(); + const router = useRouter(); + + function handleScanCreate() { + const params = new URLSearchParams(searchParams.toString()); + const timestamp = parseInt(params.get("createdAt") as string); + if (isNaN(timestamp)) { + return alert("Invalid QR Code Data (Field: createdAt)"); + } + if (checkedIn) { + return alert("User Already Checked in!"); + } else { + // TODO: make this a little more typesafe + runScanAction(scanUser?.clerkID!); + } + toast.success("Successfully Scanned User In"); + router.replace(`${path}`); + } + + return ( + <> +
+
+
+ { + const params = new URLSearchParams(searchParams.toString()); + if (!params.has("user")) { + setScanLoading(true); + const qrParsedData = superjson.parse(result); + params.set("user", qrParsedData.userID); + params.set( + "createdAt", + qrParsedData.createdAt.getTime().toString() + ); + router.replace(`${path}?${params.toString()}`); + } + }} + onError={(error) => console.log(error?.message)} + containerStyle={{ + width: "100vw", + maxWidth: "500px", + margin: "0", + }} + /> +
+ {/*
+ + + +
*/} +
+
+ router.replace(path)} + open={hasScanned || scanLoading}> + + {scanLoading ? ( + <> + + Loading Scan... + {/* */} + + + + + + ) : ( + <> + + {checkedIn ? ( + + User already checked in! + + ) : ( + <> + {!proceed ? ( + <> + + Warning! + + + {scanUser?.firstName} {scanUser?.lastName} Is not + RSVP'd + + + Do you wish to proceed? + + + + + ) : ( + <> + New Scan + + New scan for {scanUser?.firstName}{" "} + {scanUser?.lastName} + + + )} + + )} + + { + proceed ? + <> + + {!checkedIn && ( + + )} + + + : + <> + } + + )} + + + + ); +} diff --git a/apps/web/src/components/dash/admin/scanner/PassScanner.tsx b/apps/web/src/components/admin/scanner/PassScanner.tsx similarity index 84% rename from apps/web/src/components/dash/admin/scanner/PassScanner.tsx rename to apps/web/src/components/admin/scanner/PassScanner.tsx index 4b04023c..98b2f67d 100644 --- a/apps/web/src/components/dash/admin/scanner/PassScanner.tsx +++ b/apps/web/src/components/admin/scanner/PassScanner.tsx @@ -7,6 +7,9 @@ import { getScan, createScan } from "@/actions/admin/scanner-admin-actions"; import { useAction, useOptimisticAction } from "next-safe-action/hook"; import { type QRDataInterface } from "@/lib/utils/shared/qr"; import type { scansType, userType, eventType } from "@/lib/utils/shared/types"; +import c from "config"; + + import { Drawer, DrawerClose, @@ -58,6 +61,12 @@ export default function PassScanner({ const path = usePathname(); const router = useRouter(); + const register = (scanUser?.checkedIn) ? "Checked in!" : "Not Checked In"; + const guild = Object.keys(c.groups)[scanUser?.group || 0] ?? "None"; + const role = (scanUser?.role) ? scanUser?.role : "Not Found"; + + + function handleScanCreate() { const params = new URLSearchParams(searchParams.toString()); const timestamp = parseInt(params.get("createdAt") as string); @@ -123,8 +132,7 @@ export default function PassScanner({
router.replace(path)} - open={hasScanned || scanLoading} - > + open={hasScanned || scanLoading}> {scanLoading ? ( <> @@ -142,16 +150,26 @@ export default function PassScanner({ <> New Scan for {event.title} - - New scan for {scanUser?.firstName} {scanUser?.lastName} + + <> + {scanUser?.firstName} {scanUser?.lastName} + +

+ Role: {role} +

+

+ Status: {register} +

+

Guild: {guild}

- - + )} diff --git a/apps/web/src/components/dash/admin/toggles/NavItemsManager.tsx b/apps/web/src/components/admin/toggles/NavItemsManager.tsx similarity index 100% rename from apps/web/src/components/dash/admin/toggles/NavItemsManager.tsx rename to apps/web/src/components/admin/toggles/NavItemsManager.tsx diff --git a/apps/web/src/components/dash/admin/toggles/RegistrationSettings.tsx b/apps/web/src/components/admin/toggles/RegistrationSettings.tsx similarity index 100% rename from apps/web/src/components/dash/admin/toggles/RegistrationSettings.tsx rename to apps/web/src/components/admin/toggles/RegistrationSettings.tsx diff --git a/apps/web/src/components/dash/admin/toggles/ToggleItem.tsx b/apps/web/src/components/admin/toggles/ToggleItem.tsx similarity index 100% rename from apps/web/src/components/dash/admin/toggles/ToggleItem.tsx rename to apps/web/src/components/admin/toggles/ToggleItem.tsx diff --git a/apps/web/src/components/admin/users/DefaultPagination.tsx b/apps/web/src/components/admin/users/DefaultPagination.tsx new file mode 100644 index 00000000..0bc0ed47 --- /dev/null +++ b/apps/web/src/components/admin/users/DefaultPagination.tsx @@ -0,0 +1,74 @@ +"use client" + +import { + Pagination, + PaginationContent, + PaginationEllipsis, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from "@/components/shadcn/ui/pagination"; +import { useEffect, useRef, useState } from "react"; +import {usePathname,useSearchParams } from "next/navigation"; +import { createPath } from "@/lib/utils/shared/pageParams"; + +export function DefaultPagination({maxPages}:{maxPages:number}) { + // FIXME: Come back and change after done testing + + const path = usePathname(); + const params = useSearchParams(); + + + const page = params.get("page") ?? "1"; + + const [currPage,setCurrPage] = useState(+page); + const pageRef = useRef(1); + + + function incPage(){ + pageRef.current = Math.min(maxPages,pageRef.current + 1); + setCurrPage(Math.min(maxPages,currPage+1)); + } + + function decPage() { + pageRef.current = Math.max(1, pageRef.current - 1); + setCurrPage(Math.max(1, currPage - 1)); + } + + function createPaginationPath(reqPage:string){ + const url = `${path}?${reqPage}&user=${ + params.get("user") ?? "" + }&checkedBoxes=${params.get("checkedBoxes") ?? ""}`; + console.log("Pagination",url); + return url; + } + + + return ( + + + + { + decPage(); + }} + /> + + {currPage} + + { + incPage(); + }} + /> + + + + ); +} diff --git a/apps/web/src/components/admin/users/FilterCategory.tsx b/apps/web/src/components/admin/users/FilterCategory.tsx new file mode 100644 index 00000000..ee0fd2f2 --- /dev/null +++ b/apps/web/src/components/admin/users/FilterCategory.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { + AccordionItem, + AccordionTrigger, +} from "@/components/shadcn/ui/accordion"; +import FilterItem from "./FilterItem"; + +export default function FilterCategory({ + name, + items, +}: { + name: string; + items: string[]; +}) { + console.log("data:", items); + + + + return ( + <> + + {name} + {items.map((item: string) => ( + + ))} + + + ); +} diff --git a/apps/web/src/components/admin/users/FilterItem.tsx b/apps/web/src/components/admin/users/FilterItem.tsx new file mode 100644 index 00000000..d40915fb --- /dev/null +++ b/apps/web/src/components/admin/users/FilterItem.tsx @@ -0,0 +1,53 @@ +"use client" + +import { + AccordionContent, +} from "@/components/shadcn/ui/accordion"; + +import { Checkbox } from "@/components/shadcn/ui/checkbox"; +import { useSearchParams, usePathname, useRouter } from "next/navigation"; +import { useRef } from "react"; +import { createPath } from "@/lib/utils/shared/pageParams"; + + +export default function FilterItem({parentName,item}:{parentName:string,item:string}){ + const isClicked = useRef(false); + const path = usePathname(); + const params = useSearchParams(); + const router = useRouter(); + + + function handleClick(){ + if (isClicked.current) { + isClicked.current = false; + // router.push(createPath(path, '1', user, checkedBoxes)); + + const url = `${path}?${params}`; + console.log("Flipping off!"); + router.push(url); + } else { + isClicked.current = true; + console.log("Flipping on!"); + const user = params.get("user") ?? ""; + const checkedBoxes = params.get("checkedBoxes") ?? []; + // We want to filter basically + + const url = `${path}?user=${user}&checkedBoxes=${item}`; + console.log("Filter item",url); + router.push(url); + } + } + + + return( + +
+ +

{item}

+
+
+ ) +} \ No newline at end of file diff --git a/apps/web/src/components/admin/users/Filters.tsx b/apps/web/src/components/admin/users/Filters.tsx new file mode 100644 index 00000000..0d89ffb5 --- /dev/null +++ b/apps/web/src/components/admin/users/Filters.tsx @@ -0,0 +1,48 @@ +"use client"; + +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/shadcn/ui/card"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/shadcn/ui/accordion"; +import FilterCategory from "./FilterCategory"; + +type categoryMap = { + [key: string]: string[]; +}; + +const categoriesMap: categoryMap = { + Role: ["Hacker", "Volunteer", "Mentor"], + Alphabetically: ["A-Z", "Z-A"], + Timestamp: ["Most Recent", "Less Recent"], +}; + +export default function Filters() { + return ( + + + Filters + + + + {Object.keys(categoriesMap).map((name, index, category) => ( + + ))} + + + + ); +} diff --git a/apps/web/src/components/admin/users/SearchBar.tsx b/apps/web/src/components/admin/users/SearchBar.tsx new file mode 100644 index 00000000..5bd0db45 --- /dev/null +++ b/apps/web/src/components/admin/users/SearchBar.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { Input } from "@/components/shadcn/ui/input"; +import {useRouter,useSearchParams,usePathname } from "next/navigation"; +import { useRef, useState } from "react"; +import { useDebouncedCallback } from "use-debounce"; + + +import { X } from "lucide-react"; +export default function SearchBar(){ + const searchParams = useSearchParams(); + const { replace } = useRouter(); + const pathname = usePathname(); + + // We use a debouncing strategy to prevent the search from querying every single keystroke and instead will run a time after the user completes typing + const handleSearch = useDebouncedCallback((term) => { + // @ts-ignore Works perfectly fine and is apprporiate accoriding to the docs. Might be a version issue? + const params = new URLSearchParams(searchParams); + if (term) { + params.set("user", term); + } else { + params.delete("user"); + } + replace(`${pathname}?${params.toString()}`); + }, 100); + + return ( +
+ {/* Needs to clear text */} + handleSearch(e.target.value)} + /> + +
+ ); +} \ No newline at end of file diff --git a/apps/web/src/components/dash/admin/users/ServerSections.tsx b/apps/web/src/components/admin/users/ServerSections.tsx similarity index 91% rename from apps/web/src/components/dash/admin/users/ServerSections.tsx rename to apps/web/src/components/admin/users/ServerSections.tsx index 14bf63e9..70d49ad9 100644 --- a/apps/web/src/components/dash/admin/users/ServerSections.tsx +++ b/apps/web/src/components/admin/users/ServerSections.tsx @@ -1,4 +1,4 @@ -import UserInfoSection from "@/components/dash/admin/users/UserInfoSection"; +import UserInfoSection from "@/components/admin/users/UserInfoSection"; import type { UserWithAllData } from "@/lib/utils/server/types"; import { titleCase } from "title-case"; import { Button } from "@/components/shadcn/ui/button"; @@ -45,23 +45,23 @@ export async function AccountInfo({ user }: { user: UserWithAllData }) { const clerkUser = await clerkClient.users.getUser(user.clerkID); if (!clerkUser) return null; - const signInMethods = clerkUser.externalAccounts.map((account) => - titleCase(account.provider.split("_").slice(-1)[0]) - ); + // const signInMethods = clerkUser.externalAccounts.map((account) => + // titleCase(account.provider.split("_").slice(-1)[0]) + // ); - if (clerkUser.passwordEnabled) { - signInMethods.push("Password"); - } + // if (clerkUser.passwordEnabled) { + // signInMethods.push("Password"); + // } return (
- 1 ? "s" : ""}`} value={signInMethods.join(", ")} - /> + /> */}
); diff --git a/apps/web/src/components/dash/admin/users/UpdateRoleDialog.tsx b/apps/web/src/components/admin/users/UpdateRoleDialog.tsx similarity index 100% rename from apps/web/src/components/dash/admin/users/UpdateRoleDialog.tsx rename to apps/web/src/components/admin/users/UpdateRoleDialog.tsx diff --git a/apps/web/src/components/dash/admin/users/UserColumns.tsx b/apps/web/src/components/admin/users/UserColumns.tsx similarity index 100% rename from apps/web/src/components/dash/admin/users/UserColumns.tsx rename to apps/web/src/components/admin/users/UserColumns.tsx diff --git a/apps/web/src/components/dash/admin/users/UserDataTable.tsx b/apps/web/src/components/admin/users/UserDataTable.tsx similarity index 100% rename from apps/web/src/components/dash/admin/users/UserDataTable.tsx rename to apps/web/src/components/admin/users/UserDataTable.tsx diff --git a/apps/web/src/components/dash/admin/users/UserInfoSection.tsx b/apps/web/src/components/admin/users/UserInfoSection.tsx similarity index 100% rename from apps/web/src/components/dash/admin/users/UserInfoSection.tsx rename to apps/web/src/components/admin/users/UserInfoSection.tsx diff --git a/apps/web/src/components/dash/admin/scanner/CheckinScanner.tsx b/apps/web/src/components/dash/admin/scanner/CheckinScanner.tsx deleted file mode 100644 index 585ee24d..00000000 --- a/apps/web/src/components/dash/admin/scanner/CheckinScanner.tsx +++ /dev/null @@ -1,147 +0,0 @@ -"use client"; - -import { useState, useEffect } from "react"; -import { QrScanner } from "@yudiel/react-qr-scanner"; -import superjson from "superjson"; -import { getScan, checkInUser } from "@/actions/admin/scanner-admin-actions"; -import { useAction, useOptimisticAction } from "next-safe-action/hook"; -import { type QRDataInterface } from "@/lib/utils/shared/qr"; -import type { scansType, userType, eventType } from "@/lib/utils/shared/types"; -import { - Drawer, - DrawerClose, - DrawerContent, - DrawerDescription, - DrawerFooter, - DrawerHeader, - DrawerTitle, - DrawerTrigger, -} from "@/components/shadcn/ui/drawer"; -import { Button } from "@/components/shadcn/ui/button"; -import Link from "next/link"; -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. - -*/ - -interface CheckinScannerProps { - // event: eventType; - hasScanned: boolean; - // scan: scansType | null; - checkedIn: boolean | null; - scanUser: userType | null; -} - -export default function CheckinScanner({ - // event, - hasScanned, - checkedIn, - scanUser, -}: CheckinScannerProps) { - const [scanLoading, setScanLoading] = useState(false); - const { execute: runScanAction } = useAction(checkInUser, {}); - - useEffect(() => { - if (hasScanned) { - setScanLoading(false); - } - }, [hasScanned]); - - const searchParams = useSearchParams(); - const path = usePathname(); - const router = useRouter(); - - function handleScanCreate() { - const params = new URLSearchParams(searchParams.toString()); - const timestamp = parseInt(params.get("createdAt") as string); - if (isNaN(timestamp)) { - return alert("Invalid QR Code Data (Field: createdAt)"); - } - if (checkedIn) { - return alert("User Already Checked in!"); - } else { - // TODO: make this a little more typesafe - runScanAction(scanUser?.clerkID!); - } - toast.success("Successfully Scanned User In"); - router.replace(`${path}`); - } - - return ( - <> -
-
-
- { - const params = new URLSearchParams(searchParams.toString()); - if (!params.has("user")) { - setScanLoading(true); - const qrParsedData = superjson.parse(result); - params.set("user", qrParsedData.userID); - params.set("createdAt", qrParsedData.createdAt.getTime().toString()); - router.replace(`${path}?${params.toString()}`); - } - }} - onError={(error) => console.log(error?.message)} - containerStyle={{ - width: "100vw", - maxWidth: "500px", - margin: "0", - }} - /> -
- {/*
- - - -
*/} -
-
- router.replace(path)} open={hasScanned || scanLoading}> - - {scanLoading ? ( - <> - - Loading Scan... - {/* */} - - - - - - ) : ( - <> - - {checkedIn ? ( - User already checked in! - ) : ( - <> - New Scan - - New scan for {scanUser?.firstName} {scanUser?.lastName} - - - )} - - - {!checkedIn && } - - - - )} - - - - ); -} diff --git a/apps/web/src/components/dash/overview/ServerBubbles.tsx b/apps/web/src/components/dash/overview/ServerBubbles.tsx index b2ce66e4..8149a4a3 100644 --- a/apps/web/src/components/dash/overview/ServerBubbles.tsx +++ b/apps/web/src/components/dash/overview/ServerBubbles.tsx @@ -33,7 +33,7 @@ export function Questions() { export function TitleBubble() { return ( -
+

{c.hackathonName}

diff --git a/apps/web/src/components/dash/shared/DashNavItem.tsx b/apps/web/src/components/dash/shared/DashNavItem.tsx index d6ee0439..bfeaf579 100644 --- a/apps/web/src/components/dash/shared/DashNavItem.tsx +++ b/apps/web/src/components/dash/shared/DashNavItem.tsx @@ -15,7 +15,7 @@ export default function DashNavItem({ name, path }: DashNavItemProps) { + + + + + + + +
+
+ + Powered by Vercel + +
+
+ + ); +} diff --git a/apps/web/src/components/landing/MLHBadge.tsx b/apps/web/src/components/landing/MLHBadge.tsx new file mode 100644 index 00000000..16cba9e1 --- /dev/null +++ b/apps/web/src/components/landing/MLHBadge.tsx @@ -0,0 +1,45 @@ +import Link from "next/link"; +import Image from "next/image"; + +export default function MLHBadge() { + return ( + <> +
+ + Major League Hacking 2024 Hackathon Season + +
+
+ + Major League Hacking 2024 Hackathon Season + +
+ + ); +} diff --git a/apps/web/src/components/landing/PartnerCard.tsx b/apps/web/src/components/landing/PartnerCard.tsx new file mode 100644 index 00000000..3cb268a7 --- /dev/null +++ b/apps/web/src/components/landing/PartnerCard.tsx @@ -0,0 +1,84 @@ +import React from "react"; +import Link from "next/link"; +import Image from "next/image"; + + +type Partner = { + name: string; + logo: string; + url: string; + tier: string; +}; + +type colorMap = { + key:string, + value:string +} + +// const tierBorderMap = { +// [Tier.Title]: "w-[15rem] sm:w-72 md:w-72 lg:w-80 2xl:w-[19rem]", +// [Tier.Gold]: "w-[12.75rem] sm:w-[14.75rem] md:w-[16rem] lg:w-72 2xl:w-[19rem]", +// [Tier.Silver]: "w-[11rem] sm:w-52 md:w-60 lg:w-[16rem] 2xl:w-[17rem] ", +// [Tier.Bronze]: "w-32 sm:w-40 md:w-[12rem] lg:w-[14rem] 2xl:w-[16rem]", +// [Tier.Rowdy_Partner]: "w-[7rem] sm:w-32 md:w-40 lg:w-[11rem] 2xl:w-[13rem]", +// [Tier.In_Kind_Partner]: "w-[6rem] sm:w-[7rem] md:w-32 lg:w-40 2xl:w-52", +// }; + + + +const tierColorMap:{[key:string]:string} = { + ["Title Sponsor"]: "text-purple-500", + [ "Gold Sponsor"]: "text-yellow-600", + ["Silver Sponsor"]: "text-gray-400", + ["Bronze Sponsor"]: "text-amber-800", + ["Rowdy Partner"]: "text-blue-500", + ["Rowdy In-Kind"]: "text-red-500", +}; + + +function PartnerCard({partner,is_title}:{partner:Partner,is_title:boolean}) { + + const text: string = is_title + ? "text-2xl sm:text-3xl xl:text-4xl 2xl:text-[3rem]" + : "text-md sm:text-lg lg:text-xl xl:text-2xl 2xl:text-3xl"; + + const height: string = is_title + ? "h-[15rem] sm:h-[15rem] md:h-[16rem] lg:h-[20rem] xl:h-[20rem] 2xl:h-[22rem]" + : "h-[9rem] sm:h-[11rem] md:h-[11rem] lg:h-[12rem] xl:h-[14rem] 2xl:h-[17rem]"; + const image: string = is_title + ? "w-[17rem] sm:w-[17rem] md:w-[18rem] xl:w-[20rem] 2xl:w-[24rem]" + : "w-[8rem] sm:w-[10rem] md:w-[14rem] lg:w-48 xl:w-[16rem]"; + + return ( + +
+ {`${partner?.name} +
+

+ {partner?.name} +

+

+ {partner?.tier} +

+ + ); +} + +export default PartnerCard; diff --git a/apps/web/src/components/landing/Partners.tsx b/apps/web/src/components/landing/Partners.tsx new file mode 100644 index 00000000..7ec187cd --- /dev/null +++ b/apps/web/src/components/landing/Partners.tsx @@ -0,0 +1,52 @@ +import partnerData from "./partners.json"; +import PartnerCard from "./PartnerCard"; +import Image from "next/image"; + + + +type Partner = { + name: string; + logo: string; + url: string; + tier: string; +}; + + +export default async function Partners() { + // Christian Walker: Aware of weird bug from 1280px to 1286 px where background dissapears + const marathon: Partner = { + name: "Marathon", + logo: "marathon_logo.svg", + url: "https://www.marathonpetroleum.com/", + tier: "Title Sponsor", + }; + + return ( +
+
+

+ Partners Sections +

+

+ { + "See the Partners Component inside components/landing/Partners for an example" + } +

+
+ {/* Example Code of what our previous partner section looked like */} + {/*

+ A Huge Thanks To Our Rowdyhacks Partners! +

+ +
+ +
+ +
+ {partnerData.partners.map((partner: Partner) => ( + + ))} +
*/} +
+ ); +} diff --git a/apps/web/src/components/landing/Person.tsx b/apps/web/src/components/landing/Person.tsx new file mode 100644 index 00000000..12adef30 --- /dev/null +++ b/apps/web/src/components/landing/Person.tsx @@ -0,0 +1,9 @@ +export type Person = { + fname: string; //picture file name must match name with .png + lname: string; + imgLink: string; + role: string; + linkedin: string; + website: string; + github: string; +}; diff --git a/apps/web/src/components/landing/Team.tsx b/apps/web/src/components/landing/Team.tsx new file mode 100644 index 00000000..0c8961b7 --- /dev/null +++ b/apps/web/src/components/landing/Team.tsx @@ -0,0 +1,230 @@ +"use client"; +import React from "react"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "../shadcn/ui/carousel"; +import { Person } from "./Person"; +import TeamMember from "./TeamMember"; +import { Oswald } from "next/font/google"; +import { useState, useEffect } from "react"; +import Autoplay from "embla-carousel-autoplay"; +import { ArrowRight } from "lucide-react"; +import c from "config" + +const oswald = Oswald({ + variable: "--font-oswald", + subsets: ["latin"], +}); + +/** + * Creates our person and makes it seamless + * @param fname First Name + * @param lname Last Name + * @param role Role + * @param linkedin LinkedIn + * @param website Website + * @param github Github + * @returns Person + * */ +function createPerson( + fname: string, + lname: string, + role: string, + linkedin: string, + website: string, + github: string +): Person { + return { + fname: fname, + lname: lname, + imgLink: CreateImgLink(fname, lname), + role: role, + linkedin: linkedin, + website: website, + github: github, + }; +} +/** + * Helper function to find each team member's image in the public img landing folder + * @param firstname + * @param lastname + * @returns Image Link + * + */ +function CreateImgLink(firstname: string, lastname: string):string { + return `/img/landing/team/${firstname}_${lastname}.jpg`; +} + +// Insert whatever roles that you want here and add them to the roles object +const roles = { + director: "Director", + media: "Media/Design", + experience: "Experience", + logistics: "Logistics", + tech: "Tech", + pr: "PR", +}; + + + +let team: Array = [ + // add each person here. if no website, leave empty string + createPerson( + "First Name", + "Last Name", + "Position of Person", + "https://www.linkedin.com/", + "https://www.google.com/", + "https://www.github.com/" + ), + createPerson( + "First Name", + "Last Name", + roles.director, + "https://www.linkedin.com/", + "https://www.google.com/", + "https://www.github.com/" + ), + createPerson( + "First Name", + "Last Name", + roles.media, + "https://www.linkedin.com/", + "https://www.google.com/", + "https://www.github.com/" + ), + createPerson( + "First Name", + "Last Name", + roles.experience, + "https://www.linkedin.com/", + "https://www.google.com/", + "https://www.github.com/" + ), + createPerson( + "First Name", + "Last Name", + roles.logistics, + "https://www.linkedin.com/", + "https://www.google.com/", + "https://www.github.com/" + ), + createPerson( + "First Name", + "Last Name", + roles.tech, + "https://www.linkedin.com/", + "https://www.google.com/", + "https://www.github.com/" + ), + createPerson( + "First Name", + "Last Name", + roles.pr, + "https://www.linkedin.com/", + "https://www.google.com/", + "https://www.github.com/" + ), +]; + +function CarouselDefault() { + const [data_rendered, setData_rendered] = useState(false); + const plugin = React.useRef( + Autoplay({ delay: 2500, stopOnInteraction: true }) + ); + useEffect(() => { + // Basic use effect hook to check if the page has rendered + setData_rendered(true); + }, []); + + + return ( + + <> + {data_rendered ? ( + + + {team.map((p: Person, index: React.Key) => ( + + + + ))} + + {/* NOTE: Source image of carousel previous and next are modified with color prop */} + + + + ) : ( +
+

Loading...

+
+ )} + + ); +} + +function MobileTeam() { + const [data_rendered, setData_rendered] = useState(false); + + useEffect(() => { + // Basic use effect hook to check if the page has rendered + setData_rendered(true); + }, []); + + return ( + <> + {data_rendered ? ( +
+
+ {team.map((p: Person, index: React.Key) => ( + + ))} +
+ {/* Change directiom */} +
+

+ More Organizers +

+ +
+
+ ) : ( +
Loading...
+ )} + + ); +} + +export default function Team() { + return ( +
+
+

+ Demo Responsive Team Section +

+

+ {`Meet The Team That Made ${c.hackathonName} Possible!`} +

+
+
+ + +
+
+ ); +} diff --git a/apps/web/src/components/landing/TeamMember.tsx b/apps/web/src/components/landing/TeamMember.tsx new file mode 100644 index 00000000..9d8488a4 --- /dev/null +++ b/apps/web/src/components/landing/TeamMember.tsx @@ -0,0 +1,126 @@ +"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"; +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("/img/logo/hackkit.svg"); + setStyling(FallBackStyling); + }} + /> + + + + +
+
+ ); +} \ No newline at end of file diff --git a/apps/web/src/components/landing/WorkWithUs.tsx b/apps/web/src/components/landing/WorkWithUs.tsx new file mode 100644 index 00000000..da93db66 --- /dev/null +++ b/apps/web/src/components/landing/WorkWithUs.tsx @@ -0,0 +1,15 @@ + + +export default function WorkWithUs() { + return ( +
+

+ Work With Us Section +

+

+ Incentivize companies to monetarily support and other students to + volunteer to help out!{" "} +

+
+ ); +} diff --git a/apps/web/src/components/landing/partners.json b/apps/web/src/components/landing/partners.json new file mode 100644 index 00000000..f76186c7 --- /dev/null +++ b/apps/web/src/components/landing/partners.json @@ -0,0 +1,143 @@ + +{ + "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" + }, + { + "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":"UTSA COE", + "logo":"UTSA_COE.svg", + "url":"https://klesse.utsa.edu/", + "tier":"Rowdy Partner" + }, + { + "name":"UTSA Tech Store", + "logo":"rowdy_tech_logo.svg", + "url":"https://campustechnologystore.com/campustechnologystore/", + "tier":"Rowdy Partner" + }, + { + "name":"TD Synnex", + "logo":"TD_Synnex_logo.svg", + "url":"https://www.tdsynnex.com/", + "tier":"Rowdy Partner" + }, + { + "name":"Wolfram Alpha", + "logo":"wolfram_logo.svg", + "url":"https://www.wolframalpha.com/", + "tier":"Rowdy Partner" + }, + { + "name":"CodePath", + "logo":"Codepath_logo.svg", + "url":"https://www.codepath.org/", + "tier":"Rowdy Partner" + }, + { + "name":"ACM", + "logo":"ACM_logo.svg", + "url":"https://www.acm.org/", + "tier":"Rowdy Partner" + }, + { + "name":"Artea", + "logo":"Artea_logo.svg", + "url":"https://www.drinkartea.com/", + "tier":"Rowdy In-Kind" + }, + { + "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" + } + ] +} diff --git a/apps/web/src/components/settings/AccountSettings.tsx b/apps/web/src/components/settings/AccountSettings.tsx index b38df915..5e8ff13f 100644 --- a/apps/web/src/components/settings/AccountSettings.tsx +++ b/apps/web/src/components/settings/AccountSettings.tsx @@ -3,24 +3,76 @@ import { Input } from "@/components/shadcn/ui/input"; import { Button } from "@/components/shadcn/ui/button"; import { Label } from "@/components/shadcn/ui/label"; +import { toast } from "sonner"; +import { useState } from "react"; +import { users } from "db/schema"; +import { z } from "zod"; +import { useAction } from "next-safe-action/hook"; +import { modifyAccountSettings } from "@/actions/user-profile-mod"; -export default function AccountSettings() { - return ( -
-
-

Personal Information

-
-
- - -
-
- - -
- -
-
-
- ); +interface UserProps { + firstName: string; + lastName: string; +} +interface AccountSettingsProps { + user: UserProps; +} + +export default function AccountSettings({ user }: AccountSettingsProps) { + const [newFirstName, setNewFirstName] = useState(user.firstName); + const [newLastName, setNewLastName] = useState(user.lastName); + + const { execute: runModifyAccountSettings } = useAction( + modifyAccountSettings, + { + onSuccess: () => { + toast.dismiss(); + toast.success("Name updated successfully!"); + }, + onError: () => { + toast.dismiss(); + toast.error("An error occurred while updating your name!"); + }, + } + ); + + return ( +
+
+

Personal Information

+
+
+ + setNewFirstName(e.target.value)} + /> +
+
+ + setNewLastName(e.target.value)} + /> +
+ +
+
+
+ ); } diff --git a/apps/web/src/components/settings/ProfileSettings.tsx b/apps/web/src/components/settings/ProfileSettings.tsx index dea56a72..f6b5ceee 100644 --- a/apps/web/src/components/settings/ProfileSettings.tsx +++ b/apps/web/src/components/settings/ProfileSettings.tsx @@ -4,7 +4,10 @@ import { Input } from "@/components/shadcn/ui/input"; import { Button } from "@/components/shadcn/ui/button"; import { Label } from "@/components/shadcn/ui/label"; import { Textarea } from "@/components/shadcn/ui/textarea"; -import { modifyUserBioAndSkills, updateProfileImage } from "@/actions/user-profile-mod"; +import { + modifyRegistrationData, + updateProfileImage, +} from "@/actions/user-profile-mod"; import { useUser } from "@clerk/nextjs"; import { useAction } from "next-safe-action/hook"; import { toast } from "sonner"; @@ -12,101 +15,124 @@ import { useState } from "react"; import { encodeFileAsBase64 } from "@/lib/utils/shared/files"; interface ProfileSettingsProps { - bio: string; + bio: string; + university: string; } -export default function ProfileSettings({ bio }: ProfileSettingsProps) { - const [newBio, setNewBio] = useState(bio); - const [newProfileImage, setNewProfileImage] = useState(null); - const { user } = useUser(); +export default function ProfileSettings({ + bio, + university, +}: ProfileSettingsProps) { + const [newBio, setNewBio] = useState(bio); + const [newUniversity, setNewUniversity] = useState(university); + const [newProfileImage, setNewProfileImage] = useState(null); + const { user } = useUser(); - const { execute: runUpdateBioAndSkills } = useAction(modifyUserBioAndSkills, { - onSuccess: () => { - toast.dismiss(); - toast.success("Profile updated successfully!"); - }, - onError: () => { - toast.dismiss(); - toast.error("An error occurred while updating your profile!"); - }, - }); + 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: runUpdateProfileImage } = useAction(updateProfileImage, { - onSuccess: async () => { - toast.dismiss(); - await user?.setProfileImage({ file: newProfileImage }); - toast.success("Profile Photo updated successfully!"); - }, - onError: (err) => { - toast.dismiss(); - toast.error("An error occurred while updating your profile photo!"); - console.error(err); - }, - }); + const { execute: runUpdateProfileImage } = useAction(updateProfileImage, { + onSuccess: async () => { + toast.dismiss(); + await user?.setProfileImage({ file: newProfileImage }); + toast.success("Profile Photo updated successfully!"); + }, + onError: (err) => { + toast.dismiss(); + toast.error("An error occurred while updating your profile photo!"); + console.error(err); + }, + }); - const handleFileChange = (event: React.ChangeEvent) => { - const file = event.target.files ? event.target.files[0] : null; - setNewProfileImage(file); - }; + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files ? event.target.files[0] : null; + setNewProfileImage(file); + }; - return ( -
-
-

Profile Photo

-
-
- - -
- -
-
-
-

Profile Data

-
-
- -