Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into HK-173-remove-bootstrap
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobellerbrock committed Dec 2, 2024
2 parents 285b838 + 5253bc9 commit 3ef3aa8
Show file tree
Hide file tree
Showing 35 changed files with 2,122 additions and 273 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ yarn-error.log*

# vscode
.vscode

#Jetbrians
.idea
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
</p>
<h1 align="center" style="margin-top: 0px;font-weight:800">HackKit</h1>
<h3 align="center"><b>Feature-packed Hackathon Managment Software That Just Works.</b><br></h3><br/>
<p>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. <i>HackKit</i> 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, <i>HackKit</i> has you covered. </p>
<p>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. <i>HackKit</i> 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, <i>HackKit</i> has you covered. </p>
<p>Want to get started with using <i>HackKit</i>? Check out our <a href="https://oss-acmutsa.vercel.app/">OSS Documents</a> that will be updated as we add new features and fix bugs! </p>
<p>
Have any questions, feedback, or need help hosting? Join our <a href="https://discord.acmutsa.org">Discord</a> or <a href="mailto:[email protected]"> Contact our projects officer.</a></p></p>

**The offical website for RowdyHacks 2024!**

## Technologies Used

- TypeScript
Expand All @@ -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

<a href="https://github.com/acmutsa/RowdyHacks24/graphs/contributors">
<img src="https://contrib.rocks/image?repo=acmutsa/RowdyHacks24" />
<a href="https://github.com/acmutsa/HackKit/graphs/contributors">
<img src="https://contrib.rocks/image?repo=acmutsa/HackKit" />
</a>

[📣❤️ Other Contributions ❤️📣](https://github.com/UTSA-ACM/RowdyHacks24/blob/dev/contributions.md)
22 changes: 17 additions & 5 deletions apps/web/src/actions/admin/scanner-admin-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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
.schema(
z.object({
Expand Down Expand Up @@ -65,12 +66,23 @@ export const getScan = adminAction
},
);

export const checkInUser = adminAction
.schema(z.string())
.action(async ({ parsedInput: 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
return await db
await db
.update(userCommonData)
.set({ checkinTimestamp: sql`now()` })
.where(eq(userCommonData.clerkID, user));
.where(eq(userCommonData.clerkID, userID));
});
189 changes: 166 additions & 23 deletions apps/web/src/actions/user-profile-mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,198 @@
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
.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({
bio: z.string().max(500),
skills: z.string().max(100),
resume: z.string(),
}),
)
.action(async ({ parsedInput: { bio, skills }, ctx: { userId } }) => {
const user = await getUser(userId);
if (!user)
returnValidationErrors(z.null(), { _errors: ["User not found"] });

.action(async ({ parsedInput: { resume }, ctx: { userId } }) => {
await db
.update(userCommonData)
.set({ bio })
.where(eq(userCommonData.clerkID, user.clerkID));
return { success: true, newbio: bio };
.update(userHackerData)
.set({ resume })
.where(eq(userHackerData.clerkID, userId));
return {
success: true,
newResume: resume,
};
});

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,
};
},
);

// 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 }, ctx: { userId } }) => {
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 })
.set({
firstName,
lastName,
hackerTag,
isSearchable: hasSearchableProfile,
})
.where(eq(userCommonData.clerkID, userId));
return {
success: true,
newFirstName: firstName,
newLastName: lastName,
newHackerTag: hackerTag,
newHasSearchableProfile: hasSearchableProfile,
};
},
);
Expand All @@ -60,11 +204,10 @@ export const updateProfileImage = authenticatedAction
.action(
async ({ parsedInput: { fileBase64, fileName }, ctx: { userId } }) => {
const image = await decodeBase64AsFile(fileBase64, fileName);
const user = await getUser(userId);
if (!user)
returnValidationErrors(z.null(), {
_errors: ["User not found"],
});
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",
Expand All @@ -73,7 +216,7 @@ export const updateProfileImage = authenticatedAction
.update(userCommonData)
.set({ profilePhoto: blobUpload.url })
.where(eq(userCommonData.clerkID, user.clerkID));
revalidatePath("/settings/profile");
revalidatePath("/settings#profile");
return { success: true };
},
);
4 changes: 3 additions & 1 deletion apps/web/src/app/admin/check-in/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export default async function Page({
);

const scanUser = await getUser(searchParams.user);
if (!scanUser)
console.log(scanUser);
if (!scanUser) {
return (
<div>
<CheckinScanner
Expand All @@ -30,6 +31,7 @@ export default async function Page({
/>
</div>
);
}

return (
<div>
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/app/api/admin/events/create/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 16 additions & 4 deletions apps/web/src/app/dash/schedule/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
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";
export default async function Page() {
const sched = await getAllEvents();
const userTimeZoneHeaderKey = headers().get(VERCEL_IP_TIMEZONE_HEADER_KEY);
const userTimeZone = getClientTimeZone(userTimeZoneHeaderKey);
return (
<Suspense fallback={<Loading />}>
<UserScheduleView />
</Suspense>
<>
<h1 className="mx-auto my-8 w-3/4 text-8xl font-black">Schedule</h1>
<Suspense fallback={<Loading />}>
{/* <UserScheduleView /> */}
<ScheduleTimeline schedule={sched} timezone={userTimeZone} />
</Suspense>
</>
);
}

Expand Down
Loading

0 comments on commit 3ef3aa8

Please sign in to comment.