diff --git a/apps/bot/bot.ts b/apps/bot/bot.ts index 065c6419..b90d40c8 100644 --- a/apps/bot/bot.ts +++ b/apps/bot/bot.ts @@ -15,9 +15,8 @@ import { serve } from "bun"; import c from "config"; import { db } from "db"; import { eq } from "db/drizzle"; -import { discordVerification, users } from "db/schema"; +import { discordVerification, userCommonData } from "db/schema"; import { nanoid } from "nanoid"; -import { z } from "zod"; /* DISCORD BOT */ @@ -199,8 +198,9 @@ app.post("/api/checkDiscordVerification", async (h) => { return h.json({ success: false }); } console.log("got here 2"); - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, verification.clerkID), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, verification.clerkID), + with: { hackerData: true }, }); console.log("got here 2 with user", user); if (!user) { @@ -210,7 +210,7 @@ app.post("/api/checkDiscordVerification", async (h) => { const { discordRole: userGroupRoleName } = ( c.groups as Record - )[Object.keys(c.groups)[user.group]]; + )[Object.keys(c.groups)[user.hackerData.group]]; const guild = client.guilds.cache.get(verification.guild); if (!guild) { @@ -228,7 +228,7 @@ app.post("/api/checkDiscordVerification", async (h) => { if (!role || !userGroupRole) { console.log( "failed cause could not find a role, was looking for group " + - user.group + + user.hackerData.group + " called " + userGroupRoleName, ); diff --git a/apps/web/src/actions/admin/scanner-admin-actions.ts b/apps/web/src/actions/admin/scanner-admin-actions.ts index 16389437..2faeb2e3 100644 --- a/apps/web/src/actions/admin/scanner-admin-actions.ts +++ b/apps/web/src/actions/admin/scanner-admin-actions.ts @@ -2,8 +2,8 @@ import { adminAction } from "@/lib/safe-action"; import { z } from "zod"; -import { db } from "db"; -import { scans, users } from "db/schema"; +import { db, sql } from "db"; +import { scans, userCommonData } from "db/schema"; import { eq, and } from "db/drizzle"; export const createScan = adminAction( z.object({ @@ -46,22 +46,10 @@ export const getScan = adminAction( }, ); -export const checkInUser = adminAction( - z.string(), - async (user, { userId: adminUserID }) => { - // Check if scanner is an admin - const isAdmin = ["admin", "super_admin"].includes( - ( - await db - .select({ role: users.role }) - .from(users) - .where(eq(users.clerkID, adminUserID)) - )[0].role, - ); - // Set checkedIn to true - return await db - .update(users) - .set({ checkedIn: true }) - .where(eq(users.clerkID, user)); - }, -); +export const checkInUser = adminAction(z.string(), async (user) => { + // Set checkinTimestamp + return await db + .update(userCommonData) + .set({ checkinTimestamp: sql`now()` }) + .where(eq(userCommonData.clerkID, user)); +}); diff --git a/apps/web/src/actions/admin/user-actions.ts b/apps/web/src/actions/admin/user-actions.ts index 31e5e649..0e650697 100644 --- a/apps/web/src/actions/admin/user-actions.ts +++ b/apps/web/src/actions/admin/user-actions.ts @@ -3,7 +3,7 @@ import { adminAction } from "@/lib/safe-action"; import { z } from "zod"; import { perms } from "config"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { db } from "db"; import { eq } from "db/drizzle"; import { revalidatePath } from "next/cache"; @@ -21,9 +21,9 @@ export const updateRole = adminAction( throw new Error("You are not allowed to do this"); } await db - .update(users) + .update(userCommonData) .set({ role: roleToSet }) - .where(eq(users.clerkID, userIDToUpdate)); + .where(eq(userCommonData.clerkID, userIDToUpdate)); revalidatePath(`/admin/users/${userIDToUpdate}`); return { success: true }; }, @@ -37,9 +37,9 @@ export const setUserApproval = adminAction( async ({ userIDToUpdate, approved }, { user, userId }) => { await db - .update(users) - .set({ approved }) - .where(eq(users.clerkID, userIDToUpdate)); + .update(userCommonData) + .set({ isApproved: approved }) + .where(eq(userCommonData.clerkID, userIDToUpdate)); revalidatePath(`/admin/users/${userIDToUpdate}`); return { success: true }; }, diff --git a/apps/web/src/actions/rsvp.ts b/apps/web/src/actions/rsvp.ts index 1e13fdf7..e71ccb82 100644 --- a/apps/web/src/actions/rsvp.ts +++ b/apps/web/src/actions/rsvp.ts @@ -4,19 +4,19 @@ import { authenticatedAction } from "@/lib/safe-action"; import { z } from "zod"; import { db } from "db"; import { eq } from "db/drizzle"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; export const rsvpMyself = authenticatedAction( z.any(), async (_, { userId }) => { - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!user) throw new Error("User not found"); await db - .update(users) - .set({ rsvp: true }) - .where(eq(users.clerkID, userId)); + .update(userCommonData) + .set({ isRSVPed: true }) + .where(eq(userCommonData.clerkID, userId)); return { success: true }; }, ); diff --git a/apps/web/src/actions/teams.ts b/apps/web/src/actions/teams.ts index dff785d0..f6f8a3dd 100644 --- a/apps/web/src/actions/teams.ts +++ b/apps/web/src/actions/teams.ts @@ -1,30 +1,27 @@ "use server"; -// TODO: update team /api enpoints to be actions +// TODO: update team /api endpoints to be actions import { authenticatedAction } from "@/lib/safe-action"; import { z } from "zod"; import { db } from "db"; -import { users, teams, invites } from "db/schema"; +import { userCommonData, userHackerData, teams, invites } from "db/schema"; import { eq } from "db/drizzle"; import { revalidatePath } from "next/cache"; -import { toCalendar } from "@internationalized/date"; export const leaveTeam = authenticatedAction( z.null(), async (_, { userId }) => { - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - with: { - team: true, - }, + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), + with: { hackerData: true }, }); if (!user) { throw new Error("User not found"); } - if (user.team === null || user.team === undefined) { + if (!user.hackerData.teamID) { revalidatePath("/dash/team"); return { success: false, @@ -34,13 +31,17 @@ export const leaveTeam = authenticatedAction( const result = await db.transaction(async (tx) => { await tx - .update(users) + .update(userHackerData) .set({ teamID: null }) - .where(eq(users.clerkID, userId)); + .where(eq(userHackerData.clerkID, user.clerkID)); const team = await tx.query.teams.findFirst({ - where: eq(teams.id, user.team?.id as string), // Added null check for user.team. Converted to string since TS does not realise for some reason that we checked above. + where: eq(teams.id, user.hackerData.teamID as string), // Converted to string since TS does not realise for some reason that we checked above. with: { - members: true, + members: { + with: { + commonData: true, + }, + }, }, }); @@ -71,7 +72,7 @@ export const leaveTeam = authenticatedAction( revalidatePath("/dash/team"); return { success: true, - message: `Team has been left. Ownership has been transferred to ${team.members[0].firstName} ${team.members[0].lastName}.`, + message: `Team has been left. Ownership has been transferred to ${team.members[0].commonData.firstName} ${team.members[0].commonData.lastName}.`, }; } revalidatePath("/dash/team"); diff --git a/apps/web/src/actions/user-profile-mod.ts b/apps/web/src/actions/user-profile-mod.ts index 9c73942c..fa0b3b29 100644 --- a/apps/web/src/actions/user-profile-mod.ts +++ b/apps/web/src/actions/user-profile-mod.ts @@ -3,7 +3,7 @@ import { authenticatedAction } from "@/lib/safe-action"; import { z } from "zod"; import { db } from "db"; -import { users, profileData, registrationData } from "db/schema"; +import { userCommonData } from "db/schema"; import { eq } from "db/drizzle"; import { put } from "@vercel/blob"; import { decodeBase64AsFile } from "@/lib/utils/shared/files"; @@ -16,14 +16,14 @@ export const modifyRegistrationData = authenticatedAction( skills: z.string().max(100), }), async ({ bio, skills }, { userId }) => { - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!user) throw new Error("User not found"); await db - .update(profileData) + .update(userCommonData) .set({ bio }) - .where(eq(profileData.hackerTag, user.hackerTag)); + .where(eq(userCommonData.clerkID, user.clerkID)); return { success: true, newbio: bio }; }, ); @@ -34,14 +34,14 @@ export const modifyAccountSettings = authenticatedAction( lastName: z.string().min(1).max(50), }), async ({ firstName, lastName }, { userId }) => { - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!user) throw new Error("User not found"); await db - .update(users) + .update(userCommonData) .set({ firstName, lastName }) - .where(eq(users.clerkID, userId)); + .where(eq(userCommonData.clerkID, userId)); return { success: true, newFirstName: firstName, @@ -54,16 +54,16 @@ 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), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!user) throw new Error("User not found"); const blobUpload = await put(image.name, image, { access: "public" }); await db - .update(profileData) + .update(userCommonData) .set({ profilePhoto: blobUpload.url }) - .where(eq(profileData.hackerTag, user.hackerTag)); + .where(eq(userCommonData.clerkID, user.clerkID)); revalidatePath("/settings/profile"); return { success: true }; }, diff --git a/apps/web/src/app/admin/check-in/page.tsx b/apps/web/src/app/admin/check-in/page.tsx index 2bbdafea..6069e5ec 100644 --- a/apps/web/src/app/admin/check-in/page.tsx +++ b/apps/web/src/app/admin/check-in/page.tsx @@ -1,83 +1,64 @@ import CheckinScanner from "@/components/admin/scanner/CheckinScanner"; -import FullScreenMessage from "@/components/shared/FullScreenMessage"; import { db } from "db"; -import { eq, and } from "db/drizzle"; -import { events, users, scans } from "db/schema"; +import { eq } from "db/drizzle"; +import { userCommonData } from "db/schema"; export default async function Page({ searchParams, }: { searchParams: { [key: string]: string | undefined }; }) { - // TODO: maybe move event existant check into a layout so it holds state? - - // if (!params || !params.id || isNaN(parseInt(params.id))) { - // return ( - // - // ); - // } - - // const event = await db.query.events.findFirst({ - // where: eq(events.id, parseInt(params.id)), - // }); - - // if (!event) { - // return ( - // - // ); - // } - - // Returns only if search params exist - if (searchParams.user) { - 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, null]; - } - const scan = await tx - .select({ - isChecked: users.checkedIn, - hasRSVPed: users.rsvp, - }) - .from(users) - .where(eq(users.clerkID, searchParams.user!)); - if (scan) { - return [scan[0].isChecked, scanUser, scan[0].hasRSVPed]; - } else { - return [null, scanUser, null]; - } - }, - ); - + // Check for missing searchParams + if (!searchParams.user) { return (
); } - // Fall through case + const [isChecked, scanUser, hasRSVPed] = await db.transaction( + async (tx) => { + const scanUser = await tx.query.userCommonData.findFirst({ + where: eq( + userCommonData.clerkID, + searchParams.user ?? "unknown", + ), + }); + if (!scanUser) { + return [null, null, null]; + } + const scan = await tx + .select({ + checkinTimestamp: userCommonData.checkinTimestamp, + hasRSVPed: userCommonData.isRSVPed, + }) + .from(userCommonData) + .where(eq(userCommonData.clerkID, searchParams.user!)); + if (scan) { + return [ + scan[0].checkinTimestamp != null, + scanUser, + scan[0].hasRSVPed, + ]; + } else { + return [null, scanUser, null]; + } + }, + ); + return (
); diff --git a/apps/web/src/app/admin/layout.tsx b/apps/web/src/app/admin/layout.tsx index 9cae4ee7..3ac49e5b 100644 --- a/apps/web/src/app/admin/layout.tsx +++ b/apps/web/src/app/admin/layout.tsx @@ -6,13 +6,12 @@ import Link from "next/link"; import { Button } from "@/components/shadcn/ui/button"; import DashNavItem from "@/components/dash/shared/DashNavItem"; import { eq } from "db/drizzle"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import FullScreenMessage from "@/components/shared/FullScreenMessage"; 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; @@ -25,8 +24,8 @@ export default async function AdminLayout({ children }: AdminLayoutProps) { return redirect("/sign-in"); } - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!user || (user.role !== "admin" && user.role !== "super_admin")) { diff --git a/apps/web/src/app/admin/page.tsx b/apps/web/src/app/admin/page.tsx index a22768e0..32b82004 100644 --- a/apps/web/src/app/admin/page.tsx +++ b/apps/web/src/app/admin/page.tsx @@ -8,11 +8,9 @@ import { } from "@/components/shadcn/ui/card"; import { db } from "db"; import { eq, desc } from "db/drizzle"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; 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"; import { auth } from "@clerk/nextjs"; import { notFound } from "next/navigation"; @@ -25,9 +23,9 @@ export default async function Page() { if (!userId) return notFound(); - const adminUser = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - orderBy: desc(users.createdAt), + const adminUser = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), + orderBy: desc(userCommonData.signupTime), }); if ( @@ -162,10 +160,10 @@ function getRecentRegistrationData(users: userType[]) { } for (const user of users) { - if (user.rsvp) rsvpCount++; - if (user.checkedIn) checkinCount++; + if (user.isRSVPed) rsvpCount++; + if (user.checkinTimestamp) checkinCount++; - const stamp = user.createdAt.toISOString().split("T")[0]; + const stamp = user.signupTime.toISOString().split("T")[0]; if (recentSignupCount[stamp] != undefined) recentSignupCount[stamp]++; } @@ -174,7 +172,7 @@ function getRecentRegistrationData(users: userType[]) { } async function getUsers() { - const usersReq = await db.query.users.findMany(); + const usersReq = await db.query.userCommonData.findMany(); return usersReq; } diff --git a/apps/web/src/app/admin/scanner/[id]/page.tsx b/apps/web/src/app/admin/scanner/[id]/page.tsx index b48b2b49..13a9641a 100644 --- a/apps/web/src/app/admin/scanner/[id]/page.tsx +++ b/apps/web/src/app/admin/scanner/[id]/page.tsx @@ -2,7 +2,7 @@ import PassScanner from "@/components/admin/scanner/PassScanner"; import FullScreenMessage from "@/components/shared/FullScreenMessage"; import { db } from "db"; import { eq, and } from "db/drizzle"; -import { events, users, scans } from "db/schema"; +import { events, userCommonData, scans } from "db/schema"; export default async function Page({ params, @@ -35,45 +35,53 @@ export default async function Page({ ); } - if (searchParams.user) { - const [scan, scanUser] = await db.transaction(async (tx) => { - const scanUser = await tx.query.users.findFirst({ - where: eq(users.clerkID, searchParams.user!), - }); - if (!scanUser) { - return [null, null]; - } - const scan = await tx.query.scans.findFirst({ - where: and( - eq(scans.eventID, event.id), - eq(scans.userID, scanUser.clerkID), - ), - }); - if (scan) { - return [scan, scanUser]; - } else { - return [null, scanUser]; - } - }); + if (!searchParams.user) { return (
); } + const [scan, scanUser] = await db.transaction(async (tx) => { + const scanUser = await tx.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, searchParams.user!), + with: { + hackerData: { + with: { + team: true, + }, + }, + }, + }); + if (!scanUser) { + return [null, null]; + } + const scan = await tx.query.scans.findFirst({ + where: and( + eq(scans.eventID, event.id), + eq(scans.userID, scanUser.clerkID), + ), + }); + if (scan) { + return [scan, scanUser]; + } else { + return [null, scanUser]; + } + }); + return (
); diff --git a/apps/web/src/app/admin/users/[slug]/page.tsx b/apps/web/src/app/admin/users/[slug]/page.tsx index b65a0622..48756a5d 100644 --- a/apps/web/src/app/admin/users/[slug]/page.tsx +++ b/apps/web/src/app/admin/users/[slug]/page.tsx @@ -1,5 +1,5 @@ import { db } from "db"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { eq } from "db/drizzle"; import Image from "next/image"; import { Button } from "@/components/shadcn/ui/button"; @@ -25,19 +25,15 @@ export default async function Page({ params }: { params: { slug: string } }) { if (!userId) return notFound(); - const admin = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const admin = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!admin || !isUserAdmin(admin)) return notFound(); - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, params.slug), - with: { - profileData: true, - registrationData: true, - team: true, - }, + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, params.slug), + with: { hackerData: { with: { team: true } } }, }); if (!user) { @@ -70,7 +66,7 @@ export default async function Page({ params }: { params: { slug: string } }) { {(c.featureFlags.core.requireUsersApproval as boolean) && ( )} @@ -81,7 +77,7 @@ export default async function Page({ params }: { params: { slug: string } }) { {`Profile @@ -91,11 +87,11 @@ export default async function Page({ params }: { params: { slug: string } }) {

@{user.hackerTag}

- {/*

{team.bio}

*/} + {/*

{team.bio}

*/}
Joined{" "} - {user.createdAt + {user.signupTime .toDateString() .split(" ") .slice(1) diff --git a/apps/web/src/app/admin/users/page.tsx b/apps/web/src/app/admin/users/page.tsx index b1b17c23..5ea82481 100644 --- a/apps/web/src/app/admin/users/page.tsx +++ b/apps/web/src/app/admin/users/page.tsx @@ -5,9 +5,7 @@ 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"; +import { userCommonData } from "db/schema"; export default async function Page({ searchParams, @@ -27,15 +25,12 @@ export default async function Page({ 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, - }, + const userData = await db.query.userCommonData.findMany({ + with: { hackerData: true }, where: and( or( - ilike(users.firstName, `%${user}%`), - ilike(users.lastName, `%${user}%`), + ilike(userCommonData.firstName, `%${user}%`), + ilike(userCommonData.lastName, `%${user}%`), ), ), }); diff --git a/apps/web/src/app/api/admin/events/create/route.ts b/apps/web/src/app/api/admin/events/create/route.ts index 67152cda..e4c1fc34 100644 --- a/apps/web/src/app/api/admin/events/create/route.ts +++ b/apps/web/src/app/api/admin/events/create/route.ts @@ -1,7 +1,7 @@ import { auth } from "@clerk/nextjs"; import { eq } from "db/drizzle"; import { db } from "db"; -import { users, events } from "db/schema"; +import { userCommonData, events } from "db/schema"; import { newEventFormSchema } from "@/validators/event"; import { BasicRedirValidator } from "@/validators/shared/basicRedir"; import { NextResponse } from "next/server"; @@ -15,8 +15,8 @@ export async function POST(req: Request) { if (!userId) return new Response("Unauthorized", { status: 401 }); - const reqUserRecord = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const reqUserRecord = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if ( diff --git a/apps/web/src/app/api/admin/export/route.ts b/apps/web/src/app/api/admin/export/route.ts index 404a1fdb..72fe0072 100644 --- a/apps/web/src/app/api/admin/export/route.ts +++ b/apps/web/src/app/api/admin/export/route.ts @@ -1,6 +1,6 @@ import { db } from "db"; import { eq } from "db/drizzle"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { auth } from "@clerk/nextjs"; function escape(value: any) { @@ -37,8 +37,8 @@ export async function GET() { if (!userId) return new Response("Unauthorized", { status: 401 }); - const reqUserRecord = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const reqUserRecord = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if ( @@ -48,26 +48,21 @@ export async function GET() { return new Response("Unauthorized", { status: 401 }); } - const userTableData = await db.query.users.findMany({ - with: { - registrationData: true, - profileData: true, - }, + const userTableData = await db.query.userCommonData.findMany({ + with: { hackerData: true }, }); - const columed = userTableData.map((user) => { + const flattenedUsers = userTableData.map((user) => { // TODO: Have to use any here to avoid type errors as we reshape the data. Could be fixed with a better type definition. let toRet: any = { ...user, - ...user.registrationData, - ...user.profileData, + ...user.hackerData, }; - delete toRet.registrationData; - delete toRet.profileData; + delete toRet.hackerData; return toRet; }); - const csv = jsonToCSV(columed); + const csv = jsonToCSV(flattenedUsers); return new Response(csv, { headers: { diff --git a/apps/web/src/app/api/registration/create/route.ts b/apps/web/src/app/api/registration/create/route.ts index 048c8951..dfbd3a55 100644 --- a/apps/web/src/app/api/registration/create/route.ts +++ b/apps/web/src/app/api/registration/create/route.ts @@ -2,11 +2,10 @@ import { currentUser, clerkClient } from "@clerk/nextjs"; import { NextResponse } from "next/server"; import { db } from "db"; import { eq, sql } from "db/drizzle"; -import { users, registrationData, profileData } from "db/schema"; +import { userCommonData, userHackerData } from "db/schema"; import { RegisterFormValidator } from "@/validators/shared/RegisterForm"; import c from "config"; import { z } from "zod"; -import { sendEmail } from "@/lib/utils/server/ses"; export async function POST(req: Request) { const rawBody = await req.json(); @@ -51,8 +50,8 @@ export async function POST(req: Request) { // TODO: Might be removable? Not sure if this is needed. In every case, the sure should have a peice of metadata that says if they are registered or not. - const lookupByUserID = await db.query.users.findFirst({ - where: eq(users.clerkID, user.id), + const lookupByUserID = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, user.id), }); if (lookupByUserID) { @@ -65,8 +64,8 @@ export async function POST(req: Request) { ); } - const lookupByHackerTag = await db.query.users.findFirst({ - where: eq(users.hackerTag, body.hackerTag.toLowerCase()), + const lookupByHackerTag = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.hackerTag, body.hackerTag.toLowerCase()), }); if (lookupByHackerTag) { @@ -78,9 +77,9 @@ export async function POST(req: Request) { const totalUserCount = await db .select({ count: sql`count(*)`.mapWith(Number) }) - .from(users); + .from(userCommonData); - if (!body.acceptsMLHCodeOfConduct || !body.shareDataWithMLH) { + if (!body.hasAcceptedMLHCoC || !body.hasSharedDataWithMLH) { return NextResponse.json({ success: false, message: @@ -89,49 +88,45 @@ export async function POST(req: Request) { } await db.transaction(async (tx) => { - await tx.insert(users).values({ + await tx.insert(userCommonData).values({ clerkID: user.id, firstName: body.firstName, lastName: body.lastName, email: body.email, hackerTag: body.hackerTag.toLowerCase(), - registrationComplete: true, - group: totalUserCount[0].count % Object.keys(c.groups).length, - hasSearchableProfile: body.profileIsSearchable, - }); - - await tx.insert(registrationData).values({ - clerkID: user.id, - acceptedMLHCodeOfConduct: body.acceptsMLHCodeOfConduct, - accommodationNote: body.accommodationNote || null, age: body.age, - dietRestrictions: body.dietaryRestrictions, - ethnicity: body.ethnicity, gender: body.gender, - hackathonsAttended: body.hackathonsAttended, - heardFrom: body.heardAboutEvent || null, - levelOfStudy: body.levelOfStudy, - major: body.major, race: body.race, - sharedDataWithMLH: body.shareDataWithMLH, + ethnicity: body.ethnicity, shirtSize: body.shirtSize, - shortID: body.shortID, - softwareExperience: body.softwareBuildingExperience, + dietRestrictions: body.dietaryRestrictions, + accommodationNote: body.accommodationNote || null, + discord: body.profileDiscordName, + pronouns: body.pronouns, + bio: body.bio, + skills: body.skills.map((v) => v.text.toLowerCase()), + profilePhoto: user.imageUrl, + isFullyRegistered: true, + isSearchable: body.profileIsSearchable, + }); + + await tx.insert(userHackerData).values({ + clerkID: user.id, university: body.university, - wantsToReceiveMLHEmails: body.wantsToReceiveMLHEmails, + major: body.major, + schoolID: body.schoolID, + levelOfStudy: body.levelOfStudy, + hackathonsAttended: body.hackathonsAttended, + softwareExperience: body.softwareBuildingExperience, + heardFrom: body.heardAboutEvent || null, GitHub: body.github, LinkedIn: body.linkedin, PersonalWebsite: body.personalWebsite, resume: body.resume, - }); - - await tx.insert(profileData).values({ - bio: body.bio, - discordUsername: body.profileDiscordName, - hackerTag: body.hackerTag.toLowerCase(), - profilePhoto: user.imageUrl, - pronouns: body.pronouns, - skills: body.skills.map((v) => v.text.toLowerCase()), + group: totalUserCount[0].count % Object.keys(c.groups).length, + hasAcceptedMLHCoC: body.hasAcceptedMLHCoC, + hasSharedDataWithMLH: body.hasSharedDataWithMLH, + isEmailable: body.isEmailable, }); }); diff --git a/apps/web/src/app/api/team/create/route.ts b/apps/web/src/app/api/team/create/route.ts index de723958..2574fc89 100644 --- a/apps/web/src/app/api/team/create/route.ts +++ b/apps/web/src/app/api/team/create/route.ts @@ -2,7 +2,7 @@ import { auth } from "@clerk/nextjs"; import { NextResponse } from "next/server"; import { db } from "db"; import { eq } from "db/drizzle"; -import { users, teams, errorLog } from "db/schema"; +import { userCommonData, userHackerData, teams, errorLog } from "db/schema"; import { newTeamValidator } from "@/validators/shared/team"; import { nanoid } from "nanoid"; import c from "config"; @@ -12,12 +12,13 @@ export async function POST(req: Request) { const { userId } = await auth(); if (!userId) return new Response("Unauthorized", { status: 401 }); - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), + with: { hackerData: true }, }); if (!user) return new Response("Unauthorized", { status: 401 }); - if (user.teamID) { + if (user.hackerData.teamID) { return NextResponse.json({ success: false, message: @@ -47,11 +48,9 @@ export async function POST(req: Request) { ownerID: userId, }); await tx - .update(users) - .set({ - teamID, - }) - .where(eq(users.clerkID, userId)); + .update(userHackerData) + .set({ teamID }) + .where(eq(userHackerData.clerkID, userId)); }); return NextResponse.json({ success: true, diff --git a/apps/web/src/app/api/team/invite/accept/route.ts b/apps/web/src/app/api/team/invite/accept/route.ts index 81b8a328..e3e9fd31 100644 --- a/apps/web/src/app/api/team/invite/accept/route.ts +++ b/apps/web/src/app/api/team/invite/accept/route.ts @@ -2,7 +2,7 @@ import { serverZodResponse } from "@/lib/utils/server/types"; import { BasicServerValidator } from "@/validators/shared/basic"; import { db } from "db"; import { eq, and } from "db/drizzle"; -import { users, invites, teams } from "db/schema"; +import { userCommonData, userHackerData, invites, teams } from "db/schema"; import { auth } from "@clerk/nextjs"; import { NextResponse } from "next/server"; import { z } from "zod"; @@ -28,18 +28,21 @@ export async function POST( }); } - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), with: { - team: true, - invites: { - where: eq(invites.teamID, body.data.teamInviteID), + hackerData: { + with: { + invites: { + where: eq(invites.teamID, body.data.teamInviteID), + }, + }, }, }, }); if (!user) return NextResponse.json("Unauthorized", { status: 401 }); - if (user.team) { + if (user.hackerData.teamID) { return NextResponse.json({ success: false, message: "You are already on a team.", @@ -47,7 +50,7 @@ export async function POST( }); } - if (user.invites.length === 0) { + if (user.hackerData.invites.length === 0) { return NextResponse.json({ success: false, message: "You have not been invited to this team.", @@ -56,7 +59,7 @@ export async function POST( } const team = await db.query.teams.findFirst({ - where: eq(teams.id, user.invites[0].teamID), + where: eq(teams.id, user.hackerData.invites[0].teamID), with: { members: true, }, @@ -79,16 +82,17 @@ export async function POST( } await db - .update(users) - .set({ teamID: user.invites[0].teamID }) - .where(eq(users.clerkID, userId)); - // TODO: would be interesting to see if the and() could be reomved here in favor of directly looking up the composite key. + .update(userHackerData) + .set({ teamID: user.hackerData.invites[0].teamID }) + .where(eq(userHackerData.clerkID, userId)); + + // TODO: would be interesting to see if the and() could be removed here in favor of directly looking up the composite key. await db .update(invites) .set({ status: "accepted" }) .where( and( - eq(invites.teamID, user.invites[0].teamID), + eq(invites.teamID, user.hackerData.invites[0].teamID), eq(invites.inviteeID, userId), ), ); diff --git a/apps/web/src/app/api/team/invite/create/route.ts b/apps/web/src/app/api/team/invite/create/route.ts index 2577229b..f6e2ddca 100644 --- a/apps/web/src/app/api/team/invite/create/route.ts +++ b/apps/web/src/app/api/team/invite/create/route.ts @@ -1,7 +1,7 @@ import { auth } from "@clerk/nextjs"; import { db } from "db"; import { eq } from "db/drizzle"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { NextResponse } from "next/server"; import { newInviteValidator } from "@/validators/shared/team"; import { BasicServerValidator } from "@/validators/shared/basic"; @@ -13,13 +13,13 @@ export async function POST( ): serverZodResponse { const { userId } = await auth(); if (!userId) return NextResponse.json("Unauthorized", { status: 401 }); - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - with: { team: true }, + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), + with: { hackerData: { with: { team: true } } }, }); if (!user) return NextResponse.json("Unauthorized", { status: 401 }); - if (!user.teamID || !user.team) { + if (!user.hackerData.teamID || !user.hackerData.team) { return NextResponse.json( { success: false, @@ -30,7 +30,7 @@ export async function POST( ); } - if (user.team.ownerID !== userId) { + if (user.hackerData.team.ownerID !== userId) { return NextResponse.json( { success: false, @@ -49,12 +49,16 @@ export async function POST( }); } - const invitee = await db.query.users.findFirst({ - where: eq(users.hackerTag, body.data.inviteeTag), + const invitee = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.hackerTag, body.data.inviteeTag), with: { - team: true, - invites: { - where: eq(invites.teamID, user.teamID), + hackerData: { + with: { + team: true, + invites: { + where: eq(invites.teamID, user.hackerData.teamID), + }, + }, }, }, }); @@ -67,7 +71,7 @@ export async function POST( }); } - if (invitee.invites.length > 0) { + if (invitee.hackerData.invites.length > 0) { return NextResponse.json({ success: false, message: "That user already has an invite.", @@ -77,7 +81,7 @@ export async function POST( await db.insert(invites).values({ inviteeID: invitee.clerkID, - teamID: user.teamID, + teamID: user.hackerData.teamID, }); return NextResponse.json({ diff --git a/apps/web/src/app/api/team/invite/decline/route.ts b/apps/web/src/app/api/team/invite/decline/route.ts index 6c92487b..3f5164e8 100644 --- a/apps/web/src/app/api/team/invite/decline/route.ts +++ b/apps/web/src/app/api/team/invite/decline/route.ts @@ -2,7 +2,7 @@ import { auth } from "@clerk/nextjs"; import { NextResponse } from "next/server"; import { z } from "zod"; import { db } from "db"; -import { users, invites, teams } from "db/schema"; +import { userCommonData, invites } from "db/schema"; import { eq, and } from "db/drizzle"; const inviteDeclineValidator = z.object({ @@ -23,17 +23,23 @@ export async function POST(req: Request) { }); } - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + // TODO(xander): adjust logic here. null check shouldnt require a join, and invite can be queried directly + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), with: { - invites: { - where: eq(invites.teamID, body.data.teamInviteID), + hackerData: { + with: { + invites: { + where: eq(invites.teamID, body.data.teamInviteID), + }, + }, }, }, }); if (!user) return NextResponse.json("Unauthorized", { status: 401 }); + // TODO(xander): get invite using body data here to avoid joins above await db .update(invites) .set({ @@ -41,7 +47,7 @@ export async function POST(req: Request) { }) .where( and( - eq(invites.teamID, user.invites[0].teamID), + eq(invites.teamID, user.hackerData.invites[0].teamID), eq(invites.inviteeID, userId), ), ); diff --git a/apps/web/src/app/dash/layout.tsx b/apps/web/src/app/dash/layout.tsx index 428d7a29..15d16e71 100644 --- a/apps/web/src/app/dash/layout.tsx +++ b/apps/web/src/app/dash/layout.tsx @@ -12,7 +12,7 @@ import ClientToast from "@/components/shared/ClientToast"; import { TRPCReactProvider } from "@/trpc/react"; import { db } from "db"; import { eq } from "db/drizzle"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; interface DashLayoutProps { children: React.ReactNode; @@ -25,15 +25,15 @@ export default async function DashLayout({ children }: DashLayoutProps) { return redirect("/register"); } - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, clerkUser.id), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, clerkUser.id), }); if (!user) return redirect("/register"); if ( (c.featureFlags.core.requireUsersApproval as boolean) === true && - user.approved === false && + user.isApproved === false && user.role === "hacker" ) { return redirect("/i/approval"); diff --git a/apps/web/src/app/dash/page.tsx b/apps/web/src/app/dash/page.tsx index b1de2cd5..a8a6dc52 100644 --- a/apps/web/src/app/dash/page.tsx +++ b/apps/web/src/app/dash/page.tsx @@ -1,13 +1,8 @@ -// import { SignOutButton } from "@clerk/nextjs"; -// import { Button } from "@/components/shadcn/ui/button"; -// import { Suspense } from "react"; -// import Loading from "@/components/shared/Loading"; import { auth } from "@clerk/nextjs"; import { db } from "db"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { eq } from "db/drizzle"; import c from "config"; -import superjson from "superjson"; import { createQRpayload } from "@/lib/utils/shared/qr"; // HackKit Bubbles @@ -22,8 +17,8 @@ import { export default async function Page() { const { userId } = auth(); if (!userId) return null; - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!user) return null; diff --git a/apps/web/src/app/dash/pass/page.tsx b/apps/web/src/app/dash/pass/page.tsx index 613f8b0d..00ed8844 100644 --- a/apps/web/src/app/dash/pass/page.tsx +++ b/apps/web/src/app/dash/pass/page.tsx @@ -1,9 +1,8 @@ import QRCode from "react-qr-code"; import { currentUser } from "@clerk/nextjs"; -import superjson from "superjson"; import { db } from "db"; import { eq, InferModel } from "db/drizzle"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import Image from "next/image"; import c from "config"; import { format } from "date-fns"; @@ -11,17 +10,12 @@ 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; + user: InferModel; clerk: NonNullable>>; qrPayload: string; guild: string; @@ -31,8 +25,9 @@ export default async function Page() { const user = await currentUser(); if (!user) return null; - const userDbRecord = await db.query.users.findFirst({ - where: eq(users.clerkID, user.id), + const userDbRecord = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, user.id), + with: { hackerData: true }, }); if (!userDbRecord) return null; @@ -41,7 +36,7 @@ export default async function Page() { userID: user.id, createdAt: new Date(), }); - const guild = Object.keys(c.groups)[userDbRecord.group]; + const guild = Object.keys(c.groups)[userDbRecord.hackerData.group]; return (
diff --git a/apps/web/src/app/dash/team/page.tsx b/apps/web/src/app/dash/team/page.tsx index 302bca9e..c695aa7a 100644 --- a/apps/web/src/app/dash/team/page.tsx +++ b/apps/web/src/app/dash/team/page.tsx @@ -1,7 +1,7 @@ import c from "config"; import { auth } from "@clerk/nextjs"; import { db } from "db"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { eq } from "db/drizzle"; import { Button } from "@/components/shadcn/ui/button"; import Link from "next/link"; @@ -17,19 +17,23 @@ export default async function Page() { if (!userId) return null; // TODO: make this db query not so bad - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), with: { - invites: { + hackerData: { with: { - team: true, - }, - }, - team: { - with: { - members: { + team: { + with: { + members: { + with: { + commonData: true, + }, + }, + }, + }, + invites: { with: { - profileData: true, + team: true, }, }, }, @@ -38,7 +42,7 @@ export default async function Page() { }); if (!user) return null; - if (!user.teamID) { + if (!user.hackerData.teamID) { return (
@@ -70,8 +74,8 @@ export default async function Page() {

Invitations

- {user.invites.length > 0 ? ( - user.invites.map((invite) => ( + {user.hackerData.invites.length > 0 ? ( + user.hackerData.invites.map((invite) => (
); } else { - if (!user.team) return null; - const team = user.team; + if (!user.hackerData.team) return null; + const team = user.hackerData.team; return (
@@ -154,27 +158,34 @@ export default async function Page() { }} > {team.members.map((member) => ( - - + +
{`${member.hackerTag}'s

- {member.firstName}{" "} - {member.lastName} + { + member.commonData + .firstName + }{" "} + {member.commonData.lastName}

- @{member.hackerTag} + @ + { + member.commonData + .hackerTag + }

diff --git a/apps/web/src/app/discord-verify/page.tsx b/apps/web/src/app/discord-verify/page.tsx index 64af879e..bcb94527 100644 --- a/apps/web/src/app/discord-verify/page.tsx +++ b/apps/web/src/app/discord-verify/page.tsx @@ -1,5 +1,5 @@ import { db } from "db"; -import { discordVerification, users } from "db/schema"; +import { discordVerification, userCommonData } from "db/schema"; import { eq, and, or } from "db/drizzle"; import { notFound, redirect } from "next/navigation"; import { auth } from "@clerk/nextjs"; @@ -7,7 +7,6 @@ import c from "config"; import Balancer from "react-wrap-balancer"; import { MoveRight } from "lucide-react"; import Image from "next/image"; -import { Button } from "@/components/shadcn/ui/button"; import ClientToast from "@/components/shared/ClientToast"; import DiscordVerifyButton from "@/components/settings/DiscordVerifyButton"; @@ -32,8 +31,8 @@ export default async function Page({ return redirect("/sign-in"); } - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), with: { discordVerification: true, }, @@ -45,7 +44,7 @@ export default async function Page({ if ( (c.featureFlags.core.requireUsersApproval as boolean) === true && - user.approved === false && + user.isApproved === false && user.role === "hacker" ) { return redirect("/i/approval"); diff --git a/apps/web/src/app/register/page.tsx b/apps/web/src/app/register/page.tsx index 188ce88a..d7caf51a 100644 --- a/apps/web/src/app/register/page.tsx +++ b/apps/web/src/app/register/page.tsx @@ -2,7 +2,7 @@ import c from "config"; import RegisterForm from "@/components/registration/RegisterForm"; import { auth, currentUser } from "@clerk/nextjs"; import { db } from "db"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { eq } from "db/drizzle"; import { redirect } from "next/navigation"; import Navbar from "@/components/shared/Navbar"; @@ -20,8 +20,8 @@ export default async function Page() { if (!user) return redirect("/sign-up"); - const registration = await db.query.users.findFirst({ - where: eq(users.clerkID, user.id), + const registration = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, user.id), }); if (registration) { diff --git a/apps/web/src/app/rsvp/page.tsx b/apps/web/src/app/rsvp/page.tsx index 975e16c4..599473ee 100644 --- a/apps/web/src/app/rsvp/page.tsx +++ b/apps/web/src/app/rsvp/page.tsx @@ -4,14 +4,13 @@ import { auth } from "@clerk/nextjs"; import { redirect } from "next/navigation"; import { db } from "db"; import { eq } from "db/drizzle"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import ClientToast from "@/components/shared/ClientToast"; import { SignedOut, RedirectToSignIn } from "@clerk/nextjs"; import { kv } from "@vercel/kv"; import { parseRedisBoolean } from "@/lib/utils/server/redis"; import Link from "next/link"; import { Button } from "@/components/shadcn/ui/button"; -import { CheckCircleIcon } from "lucide-react"; export default async function RsvpPage({ searchParams, @@ -29,8 +28,8 @@ export default async function RsvpPage({ ); } - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!user) { @@ -39,7 +38,7 @@ export default async function RsvpPage({ if ( (c.featureFlags.core.requireUsersApproval as boolean) === true && - user.approved === false && + user.isApproved === false && user.role === "hacker" ) { return redirect("/i/approval"); @@ -53,7 +52,7 @@ export default async function RsvpPage({ rsvpEnabled as string | boolean | null | undefined, true, ) === true || - user.rsvp === true + user.isRSVPed === true ) { return ( <> @@ -66,7 +65,7 @@ export default async function RsvpPage({

RSVP

- +
); diff --git a/apps/web/src/app/settings/account/page.tsx b/apps/web/src/app/settings/account/page.tsx index c9cd28cf..3632b69b 100644 --- a/apps/web/src/app/settings/account/page.tsx +++ b/apps/web/src/app/settings/account/page.tsx @@ -1,5 +1,5 @@ import AccountSettings from "@/components/settings/AccountSettings"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { eq } from "db/drizzle"; import { auth } from "@clerk/nextjs"; import { db } from "db"; @@ -7,9 +7,9 @@ import { redirect } from "next/navigation"; export default async function Page() { const { userId } = auth(); - const user = await db.query.users.findFirst({ - with: { registrationData: true }, - where: eq(users.clerkID, userId!), + const user = await db.query.userCommonData.findFirst({ + with: { hackerData: true }, + where: eq(userCommonData.clerkID, userId!), }); if (!user) return redirect("/sign-in"); return ; diff --git a/apps/web/src/app/settings/profile/page.tsx b/apps/web/src/app/settings/profile/page.tsx index 6140f8bb..e75a1667 100644 --- a/apps/web/src/app/settings/profile/page.tsx +++ b/apps/web/src/app/settings/profile/page.tsx @@ -1,24 +1,21 @@ import ProfileSettings from "@/components/settings/ProfileSettings"; import { db } from "db"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; 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, - registrationData: true, - }, + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), + with: { hackerData: true }, }); if (!user) throw new Error("User not found"); return ( ); } diff --git a/apps/web/src/app/team/[tag]/page.tsx b/apps/web/src/app/team/[tag]/page.tsx index e1a0c5ab..9bcb1912 100644 --- a/apps/web/src/app/team/[tag]/page.tsx +++ b/apps/web/src/app/team/[tag]/page.tsx @@ -14,7 +14,7 @@ export default async function Page({ params }: { params: { tag: string } }) { with: { members: { with: { - profileData: true, + commonData: true, }, }, }, @@ -41,21 +41,22 @@ export default async function Page({ params }: { params: { tag: string } }) { )}
{team.members.map((member) => ( - +
{`${member.hackerTag}'s

- {member.firstName} {member.lastName} + {member.commonData.firstName}{" "} + {member.commonData.lastName}

- @{member.hackerTag} + @{member.commonData.hackerTag}

diff --git a/apps/web/src/app/user/[tag]/page.tsx b/apps/web/src/app/user/[tag]/page.tsx index db7a3dc7..5dd12dea 100644 --- a/apps/web/src/app/user/[tag]/page.tsx +++ b/apps/web/src/app/user/[tag]/page.tsx @@ -1,5 +1,5 @@ import { db } from "db"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { eq } from "db/drizzle"; import { notFound } from "next/navigation"; import Image from "next/image"; @@ -12,9 +12,9 @@ import Navbar from "@/components/shared/Navbar"; export default async function ({ params }: { params: { tag: string } }) { if (!params.tag || params.tag.length <= 1) return notFound(); - const user = await db.query.users.findFirst({ - where: eq(users.hackerTag, params.tag), - with: { profileData: true, registrationData: true }, + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.hackerTag, params.tag), + with: { hackerData: true }, }); if (!user) return notFound(); @@ -29,7 +29,7 @@ export default async function ({ params }: { params: { tag: string } }) {
{`@${user.hackerTag}'s @@ -43,53 +43,50 @@ export default async function ({ params }: { params: { tag: string } }) {
- {user.registrationData.GitHub && - user.registrationData.GitHub.length > 0 && ( + {user.hackerData.GitHub && + user.hackerData.GitHub.length > 0 && ( - {user.registrationData.GitHub} + {user.hackerData.GitHub} )} - {user.registrationData.LinkedIn && - user.registrationData.LinkedIn.length > 0 && ( + {user.hackerData.LinkedIn && + user.hackerData.LinkedIn.length > 0 && ( - {user.registrationData.LinkedIn} + {user.hackerData.LinkedIn} )} - {user.registrationData.PersonalWebsite && - user.registrationData.PersonalWebsite.length > - 0 && ( + {user.hackerData.PersonalWebsite && + user.hackerData.PersonalWebsite.length > 0 && ( - {user.registrationData.PersonalWebsite.replace( + {user.hackerData.PersonalWebsite.replace( "https://", "", ).replace("http://", "")} @@ -99,17 +96,12 @@ export default async function ({ params }: { params: { tag: string } }) {

About

- {user.profileData.bio} + {user.bio}

- {user.profileData.skills && - (user.profileData.skills as string[]).length > 0 ? ( + {user.skills && (user.skills as string[]).length > 0 ? ( <>

Skills

-

- {(user.profileData.skills as string[]).join( - ", ", - )} -

+

{(user.skills as string[]).join(", ")}

) : null}
diff --git a/apps/web/src/components/admin/scanner/PassScanner.tsx b/apps/web/src/components/admin/scanner/PassScanner.tsx index 0edd89ca..2fabb218 100644 --- a/apps/web/src/components/admin/scanner/PassScanner.tsx +++ b/apps/web/src/components/admin/scanner/PassScanner.tsx @@ -3,26 +3,25 @@ import { useState, useEffect } from "react"; import { QrScanner } from "@yudiel/react-qr-scanner"; import superjson from "superjson"; -import { getScan, createScan } from "@/actions/admin/scanner-admin-actions"; -import { useAction, useOptimisticAction } from "next-safe-action/hook"; +import { createScan } from "@/actions/admin/scanner-admin-actions"; +import { useAction } 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, 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"; +import { UserWithAllData } from "@/lib/utils/server/types"; /* @@ -38,7 +37,7 @@ interface PassScannerProps { event: eventType; hasScanned: boolean; scan: scansType | null; - scanUser: userType | null; + scanUser: UserWithAllData | null; } export default function PassScanner({ @@ -60,8 +59,11 @@ 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 register = scanUser?.checkinTimestamp + ? "Checked in!" + : "Not Checked In"; + const guild = + Object.keys(c.groups)[scanUser?.hackerData.group || 0] ?? "None"; const role = scanUser?.role ? scanUser?.role : "Not Found"; function handleScanCreate() { diff --git a/apps/web/src/components/admin/users/ServerSections.tsx b/apps/web/src/components/admin/users/ServerSections.tsx index 0bb58bb0..a224c138 100644 --- a/apps/web/src/components/admin/users/ServerSections.tsx +++ b/apps/web/src/components/admin/users/ServerSections.tsx @@ -13,17 +13,12 @@ export function PersonalInfo({ user }: { user: UserWithAllData }) { - - - - + + + +
); @@ -34,57 +29,51 @@ export function ProfileInfo({ user }: { user: UserWithAllData }) {
- + - +
- +
); } 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]) - // ); - - // if (clerkUser.passwordEnabled) { - // signInMethods.push("Password"); - // } + const clerkUser = await clerkClient.users + .getUser(user.clerkID) + .catch(() => {}); return (
- - - {/* 1 ? "s" : ""}`} - value={signInMethods.join(", ")} - /> */} + {clerkUser ? ( + <> + + + + ) : ( +
+ Failed to find Clerk authentication data. +
+ )}
); @@ -94,15 +83,24 @@ export function TeamInfo({ user }: { user: UserWithAllData }) { return (
- - {user.team ? ( + + {user.hackerData.team ? ( <> - - + + ) : null}
- {user.team ? ( - + {user.hackerData.team ? ( + ) : null} diff --git a/apps/web/src/components/admin/users/UserColumns.tsx b/apps/web/src/components/admin/users/UserColumns.tsx index 8a48f888..a65e1ec8 100644 --- a/apps/web/src/components/admin/users/UserColumns.tsx +++ b/apps/web/src/components/admin/users/UserColumns.tsx @@ -3,28 +3,19 @@ import { ColumnDef } from "@tanstack/react-table"; import { z } from "zod"; import { createSelectSchema } from "drizzle-zod"; -import { users, registrationData, profileData } from "db/schema"; +import { userCommonData } from "db/schema"; import Link from "next/link"; import { Button } from "@/components/shadcn/ui/button"; -const userValidator = createSelectSchema(users).merge( - z.object({ - registrationData: createSelectSchema(registrationData), - profileData: createSelectSchema(profileData).merge( - z.object({ - skills: z.array(z.string()), - }), - ), - }), -); +const userValidator = createSelectSchema(userCommonData); export type userValidatorType = Pick< z.infer, | "clerkID" - | "createdAt" + | "signupTime" | "firstName" | "lastName" - | "profileData" + | "hackerTag" | "email" | "role" >; @@ -40,9 +31,9 @@ export const columns: ColumnDef[] = [ header: "Email", }, { - accessorKey: "profileData.hackerTag", + accessorKey: "hackerTag", header: "Hacker Tag", - cell: ({ row }) => `@${row.original.profileData.hackerTag}`, + cell: ({ row }) => `@${row.original.hackerTag}`, }, { accessorKey: "clerkID", @@ -53,12 +44,12 @@ export const columns: ColumnDef[] = [ header: "Role", }, { - accessorKey: "createdAt", + accessorKey: "signupTime", header: "Signup Date", cell: ({ row }) => ( - {new Date(row.original.createdAt).toLocaleDateString() + " "} - {new Date(row.original.createdAt).toLocaleTimeString("en-US", { + {new Date(row.original.signupTime).toLocaleDateString() + " "} + {new Date(row.original.signupTime).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", })} diff --git a/apps/web/src/components/dash/overview/ServerBubbles.tsx b/apps/web/src/components/dash/overview/ServerBubbles.tsx index ec8c7e8a..e01e995b 100644 --- a/apps/web/src/components/dash/overview/ServerBubbles.tsx +++ b/apps/web/src/components/dash/overview/ServerBubbles.tsx @@ -3,13 +3,8 @@ import Link from "next/link"; import c from "config"; import { format } from "date-fns"; import GradientHero from "./GradientHero"; -// import { users } from "db/schema"; import QRCode from "react-qr-code"; -// interface MYInfoProps { -// user: typeof users.$inferSelect; -// } - export function Questions() { return (
diff --git a/apps/web/src/components/dash/shared/ProfileButton.tsx b/apps/web/src/components/dash/shared/ProfileButton.tsx index 5a31ce98..3a1eda43 100644 --- a/apps/web/src/components/dash/shared/ProfileButton.tsx +++ b/apps/web/src/components/dash/shared/ProfileButton.tsx @@ -16,7 +16,7 @@ import { import { Button } from "@/components/shadcn/ui/button"; import { auth, SignOutButton } from "@clerk/nextjs"; import { db } from "db"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { eq } from "db/drizzle"; import Link from "next/link"; import { DropdownSwitcher } from "@/components/shared/ThemeSwitcher"; @@ -25,9 +25,8 @@ export default async function ProfileButton() { const clerkUser = await auth(); const { userId } = clerkUser; if (!userId) return null; - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - with: { profileData: true }, + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!user && !userId) return null; @@ -92,10 +91,7 @@ export default async function ProfileButton() { className="relative h-8 w-8 rounded-full" > - + {user.firstName.charAt(0) + user.lastName.charAt(0)} diff --git a/apps/web/src/components/registration/RegisterForm.tsx b/apps/web/src/components/registration/RegisterForm.tsx index 26ac129c..07ebe78a 100644 --- a/apps/web/src/components/registration/RegisterForm.tsx +++ b/apps/web/src/components/registration/RegisterForm.tsx @@ -68,10 +68,10 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { dietaryRestrictions: [], profileIsSearchable: true, bio: "", - wantsToReceiveMLHEmails: false, + isEmailable: false, // The rest of these are default values to prevent the controller / uncontrolled input warning from React - acceptsMLHCodeOfConduct: false, - shareDataWithMLH: false, + hasAcceptedMLHCoC: false, + hasSharedDataWithMLH: false, accommodationNote: "", firstName: "", lastName: "", @@ -89,7 +89,7 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { pronouns: "", race: "" as any, shirtSize: "" as any, - shortID: "", + schoolID: "", university: "", }, }); @@ -106,9 +106,9 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { useEffect(() => { if (universityValue != c.localUniversityName.toLowerCase()) { - form.setValue("shortID", "NOT_LOCAL_SCHOOL"); + form.setValue("schoolID", "NOT_LOCAL_SCHOOL"); } else { - form.setValue("shortID", ""); + form.setValue("schoolID", ""); } }, [universityValue]); @@ -122,8 +122,8 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { } if ( - data.acceptsMLHCodeOfConduct !== true || - data.shareDataWithMLH !== true + data.hasAcceptedMLHCoC !== true || + data.hasSharedDataWithMLH !== true ) { setIsLoading(false); return alert( @@ -391,7 +391,7 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { ( @@ -422,7 +422,7 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { /> ( @@ -468,7 +468,7 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { /> ( @@ -700,7 +700,7 @@ export default function RegisterForm({ defaultEmail }: RegisterFormProps) { /> ( - {c.localUniversityShortIDName} + {c.localUniversitySchoolIDName} diff --git a/apps/web/src/components/settings/AccountSettings.tsx b/apps/web/src/components/settings/AccountSettings.tsx index 6bc44073..ab7269c3 100644 --- a/apps/web/src/components/settings/AccountSettings.tsx +++ b/apps/web/src/components/settings/AccountSettings.tsx @@ -5,8 +5,6 @@ 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"; diff --git a/apps/web/src/components/shared/ProfileButton.tsx b/apps/web/src/components/shared/ProfileButton.tsx index c94f47c7..1a3540a9 100644 --- a/apps/web/src/components/shared/ProfileButton.tsx +++ b/apps/web/src/components/shared/ProfileButton.tsx @@ -5,7 +5,6 @@ import { DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, - DropdownMenuShortcut, DropdownMenuTrigger, } from "@/components/shadcn/ui/dropdown-menu"; import { @@ -16,7 +15,7 @@ import { import { Button } from "@/components/shadcn/ui/button"; import { auth, SignOutButton } from "@clerk/nextjs"; import { db } from "db"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { eq } from "db/drizzle"; import Link from "next/link"; import { DropdownSwitcher } from "@/components/shared/ThemeSwitcher"; @@ -70,9 +69,8 @@ export default async function ProfileButton() { } // Make request with the clerk data that we may or may not have - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - with: { profileData: true }, + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); // If we do not have a fully fledged user, encourage them to complete registration @@ -126,10 +124,7 @@ export default async function ProfileButton() { className="relative h-8 w-8 rounded-full" > - + {user.firstName.charAt(0) + user.lastName.charAt(0)} diff --git a/apps/web/src/lib/safe-action.ts b/apps/web/src/lib/safe-action.ts index dbc0e206..e4317315 100644 --- a/apps/web/src/lib/safe-action.ts +++ b/apps/web/src/lib/safe-action.ts @@ -1,7 +1,7 @@ import { createSafeActionClient } from "next-safe-action"; import { db } from "db"; import { eq } from "db/drizzle"; -import { users } from "db/schema"; +import { userCommonData } from "db/schema"; import { auth } from "@clerk/nextjs"; export const publicAction = createSafeActionClient(); @@ -10,8 +10,8 @@ export const adminAction = createSafeActionClient({ async middleware() { const { userId } = auth(); if (!userId) throw new Error("Unauthorized (No UserID)"); - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), + const user = await db.query.userCommonData.findFirst({ + where: eq(userCommonData.clerkID, userId), }); if (!user || (user.role !== "admin" && user.role !== "super_admin")) { throw new Error("Unauthorized (Not Admin)"); diff --git a/apps/web/src/lib/utils/server/types.ts b/apps/web/src/lib/utils/server/types.ts index e2a107a8..2d587ff3 100644 --- a/apps/web/src/lib/utils/server/types.ts +++ b/apps/web/src/lib/utils/server/types.ts @@ -1,6 +1,6 @@ import { z, type ZodType } from "zod"; import type { NextResponse } from "next/server"; -import { profileData, registrationData, teams } from "db/schema"; +import { userHackerData, teams } from "db/schema"; import type { userType } from "@/lib/utils/shared/types"; export type serverZodResponse> = Promise< @@ -8,9 +8,9 @@ export type serverZodResponse> = Promise< >; export interface UserWithAllData extends userType { - profileData: typeof profileData.$inferSelect; - registrationData: typeof registrationData.$inferSelect; - team: typeof teams.$inferSelect | null; + hackerData: typeof userHackerData.$inferSelect & { + team: typeof teams.$inferSelect | null; + }; } export interface DefaultEmailTemplateProps { diff --git a/apps/web/src/lib/utils/shared/types.ts b/apps/web/src/lib/utils/shared/types.ts index 1220d6b7..1026aeef 100644 --- a/apps/web/src/lib/utils/shared/types.ts +++ b/apps/web/src/lib/utils/shared/types.ts @@ -1,6 +1,6 @@ -import { scans, users, events } from "db/schema"; +import { scans, userCommonData, events } from "db/schema"; export type DeArray = T extends (infer R)[] ? R : T; export type scansType = typeof scans.$inferSelect; -export type userType = typeof users.$inferSelect; +export type userType = typeof userCommonData.$inferSelect; export type eventType = typeof events.$inferSelect; diff --git a/apps/web/src/validators/shared/RegisterForm.ts b/apps/web/src/validators/shared/RegisterForm.ts index ba46b28f..dcd9594b 100644 --- a/apps/web/src/validators/shared/RegisterForm.ts +++ b/apps/web/src/validators/shared/RegisterForm.ts @@ -58,17 +58,17 @@ export const RegisterFormValidator = z.object({ z.literal("Hispanic or Latino", defaultPrettyError), z.literal("Not Hispanic or Latino", defaultPrettyError), ]), - acceptsMLHCodeOfConduct: z.boolean().refine((val) => val === true, { + hasAcceptedMLHCoC: z.boolean().refine((val) => val === true, { message: "You must accept the MLH Code of Conduct.", }), - shareDataWithMLH: z.boolean().refine((val) => val === true, { + hasSharedDataWithMLH: z.boolean().refine((val) => val === true, { message: "You must accept the MLH Terms & Conditions and Privacy Policy.", }), - wantsToReceiveMLHEmails: z.boolean(), + isEmailable: z.boolean(), university: z.string().min(1).max(200), major: z.string().min(1).max(200), - shortID: z + schoolID: z .string() .min(1) .max(c.localUniversityShortIDMaxLength, { diff --git a/packages/config/hackkit.config.ts b/packages/config/hackkit.config.ts index e1aed02e..8f87c101 100644 --- a/packages/config/hackkit.config.ts +++ b/packages/config/hackkit.config.ts @@ -8,7 +8,7 @@ const c = { botParticipantRole: "Participant", hackathonTimezone: "America/Chicago", localUniversityName: "The University of Texas at San Antonio", - localUniversityShortIDName: "ABC123", + localUniversitySchoolIDName: "ABC123", localUniversityShortIDMaxLength: 6, dietaryRestrictionOptions: [ "Vegan", diff --git a/packages/db/drizzle/0015_orange_garia.sql b/packages/db/drizzle/0015_orange_garia.sql new file mode 100644 index 00000000..d78bda1a --- /dev/null +++ b/packages/db/drizzle/0015_orange_garia.sql @@ -0,0 +1,135 @@ +-- Rename Tables + +ALTER TABLE "users" RENAME TO "user_common_data"; --> statement-breakpoint +ALTER TABLE "registration_data" RENAME TO "user_hacker_data"; --> statement-breakpoint + + +-- Rename Columns + +ALTER TABLE "user_hacker_data" RENAME COLUMN "short_id" TO "school_id"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" RENAME COLUMN "accepted_mlh_code_of_conduct" TO "has_accepted_mlh_coc"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" RENAME COLUMN "shared_data_with_mlh" TO "has_shared_data_with_mlh"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" RENAME COLUMN "wants_to_receive_mlh_emails" TO "is_emailable"; --> statement-breakpoint +ALTER TABLE "user_common_data" RENAME COLUMN "registration_complete" TO "is_fully_registered"; --> statement-breakpoint +ALTER TABLE "user_common_data" RENAME COLUMN "created_at" TO "signup_time"; --> statement-breakpoint +ALTER TABLE "user_common_data" RENAME COLUMN "has_searchable_profile" TO "is_searchable"; --> statement-breakpoint +ALTER TABLE "user_common_data" RENAME COLUMN "rsvp" TO "is_rsvped"; --> statement-breakpoint +ALTER TABLE "user_common_data" RENAME COLUMN "approved" TO "is_approved"; --> statement-breakpoint + + +-- Add Moving Column Destinations Without Constraints + +ALTER TABLE "user_common_data" ADD COLUMN "age" integer; --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "gender" varchar(50); --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "race" varchar(75); --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "ethnicity" varchar(50); --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "shirt_size" varchar(5); --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "diet_restrictions" json; --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "accommodation_note" text; --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "discord" varchar(60); --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "pronouns" varchar(20); --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "bio" text; --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "skills" json DEFAULT '[]'::json; --> statement-breakpoint +ALTER TABLE "user_common_data" ADD COLUMN "profile_photo" varchar(255); --> statement-breakpoint + +ALTER TABLE "user_hacker_data" ADD COLUMN "group" integer; --> statement-breakpoint +ALTER TABLE "user_hacker_data" ADD COLUMN "team_id" varchar(50); --> statement-breakpoint +ALTER TABLE "user_hacker_data" ADD COLUMN "points" integer DEFAULT 0; --> statement-breakpoint + + +-- Transfer Moving Column Data + +UPDATE "user_common_data" +SET "age" = "user_hacker_data"."age", + "gender" = "user_hacker_data"."gender", + "race" = "user_hacker_data"."race", + "ethnicity" = "user_hacker_data"."ethnicity", + "shirt_size" = "user_hacker_data"."shirt_size", + "diet_restrictions" = "user_hacker_data"."diet_restrictions", + "accommodation_note" = "user_hacker_data"."accommodation_note" +FROM "user_hacker_data" +WHERE "user_common_data"."clerk_id" = "user_hacker_data"."clerk_id"; +--> statement-breakpoint + +UPDATE "user_common_data" +SET "discord" = "profile_data"."discord_username", + "pronouns" = "profile_data"."pronouns", + "bio" = "profile_data"."bio", + "skills" = "profile_data"."skills", + "profile_photo" = "profile_data"."profile_photo" +FROM "profile_data" +WHERE "user_common_data"."hacker_tag" = "profile_data"."hacker_tag"; +--> statement-breakpoint + +UPDATE "user_hacker_data" +SET "group" = "user_common_data"."group", + "team_id" = "user_common_data"."team_id", + "points" = "user_common_data"."points" +FROM "user_common_data" +WHERE "user_common_data"."clerk_id" = "user_hacker_data"."clerk_id"; +--> statement-breakpoint + + +-- Add Moving Column Constraints + +ALTER TABLE "user_common_data" ALTER COLUMN "age" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "gender" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "race" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "ethnicity" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "shirt_size" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "diet_restrictions" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "discord" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "pronouns" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "bio" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "skills" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_common_data" ALTER COLUMN "profile_photo" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_hacker_data" ALTER COLUMN "group" SET NOT NULL; --> statement-breakpoint +ALTER TABLE "user_hacker_data" ALTER COLUMN "points" SET NOT NULL; --> statement-breakpoint + + +-- Drop Moving Column Sources + +ALTER TABLE "user_hacker_data" DROP COLUMN IF EXISTS "age"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" DROP COLUMN IF EXISTS "gender"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" DROP COLUMN IF EXISTS "race"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" DROP COLUMN IF EXISTS "ethnicity"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" DROP COLUMN IF EXISTS "shirt_size"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" DROP COLUMN IF EXISTS "diet_restrictions"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" DROP COLUMN IF EXISTS "accommodation_note"; --> statement-breakpoint +ALTER TABLE "user_common_data" DROP COLUMN IF EXISTS "group"; --> statement-breakpoint +ALTER TABLE "user_common_data" DROP COLUMN IF EXISTS "team_id"; --> statement-breakpoint +ALTER TABLE "user_common_data" DROP COLUMN IF EXISTS "points"; --> statement-breakpoint +DROP TABLE "profile_data"; --> statement-breakpoint + + +-- Update Constraints + +ALTER TABLE "chats_to_users" DROP CONSTRAINT "chats_to_users_user_id_users_clerk_id_fk"; +DO $$ BEGIN + ALTER TABLE "chats_to_users" ADD CONSTRAINT "chats_to_users_user_id_user_common_data_clerk_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_common_data"("clerk_id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint + +ALTER TABLE "tickets_to_users" DROP CONSTRAINT "tickets_to_users_user_id_users_clerk_id_fk"; +DO $$ BEGIN + ALTER TABLE "tickets_to_users" ADD CONSTRAINT "tickets_to_users_user_id_user_common_data_clerk_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_common_data"("clerk_id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint + +ALTER TABLE "user_common_data" DROP CONSTRAINT "users_email_unique"; +ALTER TABLE "user_common_data" ADD CONSTRAINT "user_common_data_email_unique" UNIQUE("email"); +--> statement-breakpoint + +ALTER TABLE "user_common_data" DROP CONSTRAINT "users_hacker_tag_unique"; +ALTER TABLE "user_common_data" ADD CONSTRAINT "user_common_data_hacker_tag_unique" UNIQUE("hacker_tag"); +--> statement-breakpoint + + +-- Drop Deleted Tables, Columns & Constraints + +ALTER TABLE "user_common_data" DROP COLUMN IF EXISTS "checked_in"; --> statement-breakpoint +ALTER TABLE "user_hacker_data" DROP CONSTRAINT "registration_data_clerk_id_unique"; --> statement-breakpoint diff --git a/packages/db/drizzle/meta/0015_snapshot.json b/packages/db/drizzle/meta/0015_snapshot.json new file mode 100644 index 00000000..1f51c682 --- /dev/null +++ b/packages/db/drizzle/meta/0015_snapshot.json @@ -0,0 +1,1022 @@ +{ + "id": "fdbe1a2d-8266-4c8c-a68d-5cb660ead4b5", + "prevId": "ad1b5139-ce4c-4c3b-9beb-56fd1f3aac91", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.chat_messages": { + "name": "chat_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.chats": { + "name": "chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "chat_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author": { + "name": "author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chats_ticket_id_tickets_id_fk": { + "name": "chats_ticket_id_tickets_id_fk", + "tableFrom": "chats", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.chats_to_users": { + "name": "chats_to_users", + "schema": "", + "columns": { + "chat_id": { + "name": "chat_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "chats_to_users_chat_id_chats_id_fk": { + "name": "chats_to_users_chat_id_chats_id_fk", + "tableFrom": "chats_to_users", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "chats_to_users_user_id_user_common_data_clerk_id_fk": { + "name": "chats_to_users_user_id_user_common_data_clerk_id_fk", + "tableFrom": "chats_to_users", + "tableTo": "user_common_data", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "clerk_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "chats_to_users_user_id_chat_id_pk": { + "name": "chats_to_users_user_id_chat_id_pk", + "columns": [ + "user_id", + "chat_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.discord_verification": { + "name": "discord_verification", + "schema": "", + "columns": { + "code": { + "name": "code", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "clerk_id": { + "name": "clerk_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "discord_user_id": { + "name": "discord_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "discord_user_tag": { + "name": "discord_user_tag", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "discord_profile_photo": { + "name": "discord_profile_photo", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "discord_name": { + "name": "discord_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "discord_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "guild": { + "name": "guild", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.error_log": { + "name": "error_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(50)", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "route": { + "name": "route", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.events": { + "name": "events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigserial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "start_time": { + "name": "start_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "host": { + "name": "host", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "events_id_unique": { + "name": "events_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + } + }, + "public.files": { + "name": "files", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "presigned_url": { + "name": "presigned_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "validated": { + "name": "validated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "type": { + "name": "type", + "type": "type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "files_id_unique": { + "name": "files_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + }, + "files_key_unique": { + "name": "files_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + } + }, + "public.invites": { + "name": "invites", + "schema": "", + "columns": { + "invitee_id": { + "name": "invitee_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "status": { + "name": "status", + "type": "invite_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "invites_invitee_id_team_id_pk": { + "name": "invites_invitee_id_team_id_pk", + "columns": [ + "invitee_id", + "team_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.scans": { + "name": "scans", + "schema": "", + "columns": { + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "event_id": { + "name": "event_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "scans_user_id_event_id_pk": { + "name": "scans_user_id_event_id_pk", + "columns": [ + "user_id", + "event_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(50)", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "bio": { + "name": "bio", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "photo": { + "name": "photo", + "type": "varchar(400)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "owner_id": { + "name": "owner_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "devpost_url": { + "name": "devpost_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "teams_id_unique": { + "name": "teams_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + }, + "teams_tag_unique": { + "name": "teams_tag_unique", + "nullsNotDistinct": false, + "columns": [ + "tag" + ] + } + } + }, + "public.tickets": { + "name": "tickets", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "ticket_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'awaiting'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.tickets_to_users": { + "name": "tickets_to_users", + "schema": "", + "columns": { + "ticket_id": { + "name": "ticket_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "tickets_to_users_ticket_id_tickets_id_fk": { + "name": "tickets_to_users_ticket_id_tickets_id_fk", + "tableFrom": "tickets_to_users", + "tableTo": "tickets", + "columnsFrom": [ + "ticket_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "tickets_to_users_user_id_user_common_data_clerk_id_fk": { + "name": "tickets_to_users_user_id_user_common_data_clerk_id_fk", + "tableFrom": "tickets_to_users", + "tableTo": "user_common_data", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "clerk_id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "tickets_to_users_user_id_ticket_id_pk": { + "name": "tickets_to_users_user_id_ticket_id_pk", + "columns": [ + "user_id", + "ticket_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.user_common_data": { + "name": "user_common_data", + "schema": "", + "columns": { + "clerk_id": { + "name": "clerk_id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "hacker_tag": { + "name": "hacker_tag", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "age": { + "name": "age", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "gender": { + "name": "gender", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "race": { + "name": "race", + "type": "varchar(75)", + "primaryKey": false, + "notNull": true + }, + "ethnicity": { + "name": "ethnicity", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "shirt_size": { + "name": "shirt_size", + "type": "varchar(5)", + "primaryKey": false, + "notNull": true + }, + "diet_restrictions": { + "name": "diet_restrictions", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "accommodation_note": { + "name": "accommodation_note", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discord": { + "name": "discord", + "type": "varchar(60)", + "primaryKey": false, + "notNull": true + }, + "pronouns": { + "name": "pronouns", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "bio": { + "name": "bio", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "skills": { + "name": "skills", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'[]'::json" + }, + "profile_photo": { + "name": "profile_photo", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "is_fully_registered": { + "name": "is_fully_registered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "signup_time": { + "name": "signup_time", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_searchable": { + "name": "is_searchable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "role": { + "name": "role", + "type": "role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'hacker'" + }, + "checkin_timestamp": { + "name": "checkin_timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "is_rsvped": { + "name": "is_rsvped", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_approved": { + "name": "is_approved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_common_data_email_unique": { + "name": "user_common_data_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "user_common_data_hacker_tag_unique": { + "name": "user_common_data_hacker_tag_unique", + "nullsNotDistinct": false, + "columns": [ + "hacker_tag" + ] + } + } + }, + "public.user_hacker_data": { + "name": "user_hacker_data", + "schema": "", + "columns": { + "clerk_id": { + "name": "clerk_id", + "type": "varchar(255)", + "primaryKey": true, + "notNull": true + }, + "university": { + "name": "university", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "major": { + "name": "major", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "schoolID": { + "name": "schoolID", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "level_of_study": { + "name": "level_of_study", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "hackathons_attended": { + "name": "hackathons_attended", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "software_experience": { + "name": "software_experience", + "type": "varchar(25)", + "primaryKey": false, + "notNull": true + }, + "heard_from": { + "name": "heard_from", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "github": { + "name": "github", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "linkedin": { + "name": "linkedin", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "personal_website": { + "name": "personal_website", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "resume": { + "name": "resume", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "default": "'https://static.acmutsa.org/No%20Resume%20Provided.pdf'" + }, + "group": { + "name": "group", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "points": { + "name": "points", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "has_accepted_mlh_coc": { + "name": "has_accepted_mlh_coc", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "has_shared_data_with_mlh": { + "name": "has_shared_data_with_mlh", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "is_emailable": { + "name": "is_emailable", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "public.chat_type": { + "name": "chat_type", + "schema": "public", + "values": [ + "ticket" + ] + }, + "public.discord_status": { + "name": "discord_status", + "schema": "public", + "values": [ + "pending", + "expired", + "accepted", + "rejected" + ] + }, + "public.type": { + "name": "type", + "schema": "public", + "values": [ + "generic", + "resume" + ] + }, + "public.invite_status": { + "name": "invite_status", + "schema": "public", + "values": [ + "pending", + "accepted", + "declined" + ] + }, + "public.role": { + "name": "role", + "schema": "public", + "values": [ + "hacker", + "volunteer", + "mentor", + "mlh", + "admin", + "super_admin" + ] + }, + "public.ticket_status": { + "name": "ticket_status", + "schema": "public", + "values": [ + "awaiting", + "in_progress", + "completed" + ] + } + }, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index 318c2baf..80bfbd0b 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -106,6 +106,13 @@ "when": 1721626835938, "tag": "0014_known_norman_osborn", "breakpoints": true + }, + { + "idx": 15, + "version": "7", + "when": 1722754144081, + "tag": "0015_orange_garia", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 2f3b18f6..5a9d535d 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -12,7 +12,6 @@ import { bigserial, text, varchar, - uniqueIndex, boolean, timestamp, integer, @@ -55,101 +54,102 @@ export const discordVerificationStatus = pgEnum("discord_status", [ "rejected", ]); -export const users = pgTable("users", { - clerkID: varchar("clerk_id", { length: 255 }) - .notNull() - .primaryKey() - .unique(), +export const userCommonData = pgTable("user_common_data", { + // id + clerkID: varchar("clerk_id", { length: 255 }).primaryKey(), + + // data firstName: varchar("first_name", { length: 50 }).notNull(), lastName: varchar("last_name", { length: 50 }).notNull(), email: varchar("email", { length: 255 }).notNull().unique(), hackerTag: varchar("hacker_tag", { length: 50 }).notNull().unique(), - registrationComplete: boolean("registration_complete") - .notNull() - .default(false), - createdAt: timestamp("created_at").notNull().defaultNow(), - hasSearchableProfile: boolean("has_searchable_profile") - .notNull() - .default(true), - group: integer("group").notNull(), + age: integer("age").notNull(), + gender: varchar("gender", { length: 50 }).notNull(), + race: varchar("race", { length: 75 }).notNull(), + ethnicity: varchar("ethnicity", { length: 50 }).notNull(), + shirtSize: varchar("shirt_size", { length: 5 }).notNull(), + dietRestrictions: json("diet_restrictions").notNull(), + accommodationNote: text("accommodation_note"), + discord: varchar("discord", { length: 60 }).notNull(), + pronouns: varchar("pronouns", { length: 20 }).notNull(), + bio: text("bio").notNull(), + skills: json("skills").notNull().$type().default([]), + profilePhoto: varchar("profile_photo", { length: 255 }).notNull(), + + // metadata + isFullyRegistered: boolean("is_fully_registered").notNull().default(false), + signupTime: timestamp("signup_time").notNull().defaultNow(), + isSearchable: boolean("is_searchable").notNull().default(true), role: roles("role").notNull().default("hacker"), checkinTimestamp: timestamp("checkin_timestamp"), - teamID: varchar("team_id", { length: 50 }), - points: integer("points").notNull().default(0), - checkedIn: boolean("checked_in").notNull().default(false), - rsvp: boolean("rsvp").notNull().default(false), - approved: boolean("approved").notNull().default(false), + isRSVPed: boolean("is_rsvped").notNull().default(false), + isApproved: boolean("is_approved").notNull().default(false), }); -export const userRelations = relations(users, ({ one, many }) => ({ - registrationData: one(registrationData, { - fields: [users.clerkID], - references: [registrationData.clerkID], - }), - discordVerification: one(discordVerification, { - fields: [users.clerkID], - references: [discordVerification.clerkID], - }), - profileData: one(profileData, { - fields: [users.hackerTag], - references: [profileData.hackerTag], - }), - files: many(files), - scans: many(scans), - team: one(teams, { - fields: [users.teamID], - references: [teams.id], +export const userCommonRelations = relations( + userCommonData, + ({ one, many }) => ({ + hackerData: one(userHackerData, { + fields: [userCommonData.clerkID], + references: [userHackerData.clerkID], + }), + discordVerification: one(discordVerification, { + fields: [userCommonData.clerkID], + references: [discordVerification.clerkID], + }), + files: many(files), + scans: many(scans), + tickets: many(ticketsToUsers), + chats: many(chatsToUsers), + messages: many(chatMessages), }), - invites: many(invites), - tickets: many(ticketsToUsers), - chats: many(chatsToUsers), - messages: many(chatMessages), -})); +); -export const registrationData = pgTable("registration_data", { - clerkID: varchar("clerk_id", { length: 255 }) - .notNull() - .primaryKey() - .unique(), - age: integer("age").notNull(), - gender: varchar("gender", { length: 50 }).notNull(), - race: varchar("race", { length: 75 }).notNull(), - ethnicity: varchar("ethnicity", { length: 50 }).notNull(), - acceptedMLHCodeOfConduct: boolean("accepted_mlh_code_of_conduct").notNull(), - sharedDataWithMLH: boolean("shared_data_with_mlh").notNull(), - wantsToReceiveMLHEmails: boolean("wants_to_receive_mlh_emails").notNull(), +export const userHackerData = pgTable("user_hacker_data", { + // id + clerkID: varchar("clerk_id", { length: 255 }).primaryKey(), + + // data university: varchar("university", { length: 200 }).notNull(), major: varchar("major", { length: 200 }).notNull(), - shortID: varchar("short_id", { length: 50 }).notNull(), + schoolID: varchar("school_id", { length: 50 }).notNull(), levelOfStudy: varchar("level_of_study", { length: 50 }).notNull(), hackathonsAttended: integer("hackathons_attended").notNull(), softwareExperience: varchar("software_experience", { length: 25, }).notNull(), heardFrom: varchar("heard_from", { length: 50 }), - shirtSize: varchar("shirt_size", { length: 5 }).notNull(), - dietRestrictions: json("diet_restrictions").notNull(), - accommodationNote: text("accommodation_note"), GitHub: varchar("github", { length: 100 }), LinkedIn: varchar("linkedin", { length: 100 }), PersonalWebsite: varchar("personal_website", { length: 100 }), resume: varchar("resume", { length: 255 }) .notNull() .default("https://static.acmutsa.org/No%20Resume%20Provided.pdf"), -}); -export const profileData = pgTable("profile_data", { - hackerTag: varchar("hacker_tag", { length: 50 }) - .notNull() - .primaryKey() - .unique(), - discordUsername: varchar("discord_username", { length: 60 }).notNull(), - pronouns: varchar("pronouns", { length: 20 }).notNull(), - bio: text("bio").notNull(), - skills: json("skills").notNull().$type().default([]), - profilePhoto: varchar("profile_photo", { length: 255 }).notNull(), + // metadata + group: integer("group").notNull(), + teamID: varchar("team_id", { length: 50 }), + points: integer("points").notNull().default(0), + hasAcceptedMLHCoC: boolean("has_accepted_mlh_coc").notNull(), + hasSharedDataWithMLH: boolean("has_shared_data_with_mlh").notNull(), + isEmailable: boolean("is_emailable").notNull(), }); +export const userHackerRelations = relations( + userHackerData, + ({ one, many }) => ({ + commonData: one(userCommonData, { + fields: [userHackerData.clerkID], + references: [userCommonData.clerkID], + }), + team: one(teams, { + fields: [userHackerData.teamID], + references: [teams.id], + }), + invites: many(invites), + }), +); + export const events = pgTable("events", { id: bigserial("id", { mode: "number" }).notNull().primaryKey().unique(), title: varchar("name", { length: 255 }).notNull(), @@ -179,9 +179,9 @@ export const files = pgTable("files", { }); export const filesRelations = relations(files, ({ one }) => ({ - owner: one(users, { + owner: one(userCommonData, { fields: [files.ownerID], - references: [users.clerkID], + references: [userCommonData.clerkID], }), })); @@ -199,9 +199,9 @@ export const scans = pgTable( ); export const scansRelations = relations(scans, ({ one }) => ({ - user: one(users, { + user: one(userCommonData, { fields: [scans.userID], - references: [users.clerkID], + references: [userCommonData.clerkID], }), event: one(events, { fields: [scans.eventID], @@ -221,7 +221,7 @@ export const teams = pgTable("teams", { }); export const teamsRelations = relations(teams, ({ one, many }) => ({ - members: many(users), + members: many(userHackerData), invites: many(invites), })); @@ -239,9 +239,9 @@ export const invites = pgTable( ); export const invitesRelations = relations(invites, ({ one }) => ({ - invitee: one(users, { + invitee: one(userHackerData, { fields: [invites.inviteeID], - references: [users.clerkID], + references: [userHackerData.clerkID], }), team: one(teams, { fields: [invites.teamID], @@ -315,9 +315,9 @@ export const chatMessageRelations = relations(chatMessages, ({ one }) => ({ fields: [chatMessages.chatID], references: [chats.id], }), - author: one(users, { + author: one(userCommonData, { fields: [chatMessages.authorID], - references: [users.clerkID], + references: [userCommonData.clerkID], }), })); @@ -329,7 +329,7 @@ export const ticketsToUsers = pgTable( .references(() => tickets.id), userID: text("user_id") .notNull() - .references(() => users.clerkID), + .references(() => userCommonData.clerkID), }, (t) => ({ pk: primaryKey({ columns: [t.userID, t.ticketID] }), @@ -341,9 +341,9 @@ export const ticketsToUserRelations = relations(ticketsToUsers, ({ one }) => ({ fields: [ticketsToUsers.ticketID], references: [tickets.id], }), - user: one(users, { + user: one(userCommonData, { fields: [ticketsToUsers.userID], - references: [users.clerkID], + references: [userCommonData.clerkID], }), })); @@ -355,7 +355,7 @@ export const chatsToUsers = pgTable( .references(() => chats.id), userID: text("user_id") .notNull() - .references(() => users.clerkID), + .references(() => userCommonData.clerkID), }, (t) => ({ pk: primaryKey({ columns: [t.userID, t.chatID] }), @@ -367,8 +367,8 @@ export const chatsToUserRelations = relations(chatsToUsers, ({ one }) => ({ fields: [chatsToUsers.chatID], references: [chats.id], }), - user: one(users, { + user: one(userCommonData, { fields: [chatsToUsers.userID], - references: [users.clerkID], + references: [userCommonData.clerkID], }), })); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2afd162..d6605f83 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - importers: .: @@ -11797,7 +11793,7 @@ packages: engines: {node: '>=6'} dev: false - /terser-webpack-plugin@5.3.10(esbuild@0.23.0)(webpack@5.93.0): + /terser-webpack-plugin@5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.93.0): resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -11814,7 +11810,8 @@ packages: optional: true dependencies: '@jridgewell/trace-mapping': 0.3.25 - esbuild: 0.23.0 + '@swc/core': 1.3.101 + esbuild: 0.19.11 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 @@ -12416,7 +12413,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(esbuild@0.23.0)(webpack@5.93.0) + terser-webpack-plugin: 5.3.10(@swc/core@1.3.101)(esbuild@0.19.11)(webpack@5.93.0) watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -12584,3 +12581,7 @@ packages: /zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} dev: false + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false