Skip to content

Commit

Permalink
Satisfies User Settings w/ New Schema (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobellerbrock authored Oct 24, 2024
1 parent 11c5602 commit 19d324c
Show file tree
Hide file tree
Showing 18 changed files with 1,731 additions and 144 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
187 changes: 165 additions & 22 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 Down
15 changes: 0 additions & 15 deletions apps/web/src/app/settings/account/page.tsx

This file was deleted.

15 changes: 9 additions & 6 deletions apps/web/src/app/settings/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ export default async function ({ children }: { children: ReactNode }) {
</div>
</div>
</div>
<div>
{/* <SettingsSection name="Settings" path="/settings" /> */}
<SettingsSection name="Account" path="/settings/account" />
<SettingsSection name="Profile" path="/settings/profile" />
</div>
<div className="col-span-4">{children}</div>
<aside className="sticky top-20 hidden h-screen md:block">
<SettingsSection name="Account" path="/settings#account" />
<SettingsSection name="Profile" path="/settings#profile" />
<SettingsSection
name="Registration"
path="/settings#registration"
/>
</aside>
<div className="col-span-4 mb-20 ml-5">{children}</div>
</div>
</>
);
Expand Down
33 changes: 31 additions & 2 deletions apps/web/src/app/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
export default function Page() {
return <span>howdy</span>;
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 (
<main>
<Header tag="Account" />
<AccountSettings user={user} />
<Header tag="Profile" />
<ProfileSettings profile={user} />
<Header tag={"Registration"} />
<RegistrationSettings />
</main>
);
}

function Header({ tag }: { tag: string }) {
return (
<h1 id={tag.toLowerCase()} className="mt-10 pb-5 text-4xl font-bold">
{tag}
</h1>
);
}
19 changes: 0 additions & 19 deletions apps/web/src/app/settings/profile/page.tsx

This file was deleted.

15 changes: 15 additions & 0 deletions apps/web/src/app/settings/registration/page.tsx
Original file line number Diff line number Diff line change
@@ -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 <RegistrationFormSettings user={user} data={hackerData} />;
}
4 changes: 0 additions & 4 deletions apps/web/src/components/registration/RegisterForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,6 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) {
}
}, [universityValue]);

useEffect(() => {
console.log(countryValue);
}, [countryValue]);

async function onSubmit(data: z.infer<typeof RegisterFormValidator>) {
console.log(data);
setIsLoading(true);
Expand Down
Loading

0 comments on commit 19d324c

Please sign in to comment.