diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 00000000..c0c6c8c0 --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,28 @@ +# .github/workflows/prettier.yml + +name: Prettier Check + +on: [pull_request] + +jobs: + prettier-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: "20" + + - name: Install pnpm + run: | + npm install -g pnpm + + - name: Install dependencies + run: pnpm install + + - name: Run Prettier check + run: pnpm run format-check diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..fa984040 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +packages/db/drizzle/meta/** +pnpm-lock.yaml +pnpm-workspace.yaml +**/package.json \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..e214cf23 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"], + "tailwindConfig": "./apps/web/tailwind.config.js", + "semi": true, + "singleQuote": false, + "jsxSingleQuote": false, + "printWidth": 80, + "tabWidth": 4, + "useTabs": true +} diff --git a/README.md b/README.md index fee4ed91..fed81ba3 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,10 @@ ## Technologies Used -- TypeScript -- Next.js -- Drizzle ORM -- Tailwind CSS +- TypeScript +- Next.js +- Drizzle ORM +- Tailwind CSS [![Vercel](https://static.rowdyhacks.org/img/powered-by-vercel.svg)](https://vercel.com/?utm_source=ACM%20UTSA&utm_campaign=oss) diff --git a/apps/backup/src/backup.ts b/apps/backup/src/backup.ts index 9cab61d3..486bdc0b 100644 --- a/apps/backup/src/backup.ts +++ b/apps/backup/src/backup.ts @@ -25,7 +25,7 @@ const uploadToS3 = async ({ name, path }: { name: string; path: string }) => { Bucket: bucket, Key: name, Body: createReadStream(path), - }) + }), ); console.log("Backup uploaded to S3..."); @@ -35,14 +35,17 @@ const dumpToFile = async (path: string) => { console.log("Dumping DB to file..."); await new Promise((resolve, reject) => { - exec(`pg_dump ${env.BACKUP_DATABASE_URL} -F t | gzip > ${path}`, (error, stdout, stderr) => { - if (error) { - reject({ error: JSON.stringify(error), stderr }); - return; - } - - resolve(undefined); - }); + exec( + `pg_dump ${env.BACKUP_DATABASE_URL} -F t | gzip > ${path}`, + (error, stdout, stderr) => { + if (error) { + reject({ error: JSON.stringify(error), stderr }); + return; + } + + resolve(undefined); + }, + ); }); console.log("DB dumped to file..."); diff --git a/apps/backup/src/index.ts b/apps/backup/src/index.ts index 854fd888..268f1b4e 100644 --- a/apps/backup/src/index.ts +++ b/apps/backup/src/index.ts @@ -31,6 +31,9 @@ job.start(); console.log("Backup cron scheduled..."); -serve({ fetch: app.fetch, port: process.env.PORT ? parseInt(process.env.PORT) : 3000 }); +serve({ + fetch: app.fetch, + port: process.env.PORT ? parseInt(process.env.PORT) : 3000, +}); console.log("Server started..."); diff --git a/apps/backup/tsconfig.json b/apps/backup/tsconfig.json index 303cdbe6..2bc3bb7b 100644 --- a/apps/backup/tsconfig.json +++ b/apps/backup/tsconfig.json @@ -1,11 +1,11 @@ { - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "outDir": "./dist", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true - } - } \ No newline at end of file + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "outDir": "./dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +} diff --git a/apps/bot/bot.ts b/apps/bot/bot.ts index f142c00d..065c6419 100644 --- a/apps/bot/bot.ts +++ b/apps/bot/bot.ts @@ -33,7 +33,9 @@ const client = new Client({ client.commands = new Collection(); const commandsPath = path.join(__dirname, "commands"); -const commandFiles = readdirSync(commandsPath).filter((file) => file.endsWith(".ts")); +const commandFiles = readdirSync(commandsPath).filter((file) => + file.endsWith(".ts"), +); for (const file of commandFiles) { console.log(`[Loading Command] ${file}`); const filePath = path.join(commandsPath, file); @@ -43,18 +45,22 @@ for (const file of commandFiles) { client.commands.set(command.data.name, command); } else { console.log( - `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.` + `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`, ); - } + } } console.log(`Loaded ${client.commands.size} Commands`); client.on(Events.InteractionCreate, async (interaction) => { if (interaction.isChatInputCommand()) { - const command = interaction.client.commands.get(interaction.commandName); + const command = interaction.client.commands.get( + interaction.commandName, + ); if (!command) { - console.error(`No command matching ${interaction.commandName} was found.`); + console.error( + `No command matching ${interaction.commandName} was found.`, + ); return; } @@ -132,9 +138,13 @@ app.get("/postMsgToServer", (h) => { .setColor(0x0099ff) .setTitle("Verification") .setURL(c.siteUrl) - .setAuthor({ name: c.botName, iconURL: c.siteUrl + c.icon.md, url: c.siteUrl }) + .setAuthor({ + name: c.botName, + iconURL: c.siteUrl + c.icon.md, + url: c.siteUrl, + }) .setDescription( - `**Verify your registration for ${c.hackathonName} ${c.itteration} to gain access to the rest of the server!**\n\nClick the "verify" button below to begin the verification process.\n\u200B` + `**Verify your registration for ${c.hackathonName} ${c.itteration} to gain access to the rest of the server!**\n\nClick the "verify" button below to begin the verification process.\n\u200B`, ) .setThumbnail(`${c.siteUrl}${c.icon.md}`) .setFooter({ @@ -145,7 +155,7 @@ app.get("/postMsgToServer", (h) => { const channel = client.channels.cache.get( serverType === "dev" ? (process.env.DISCORD_DEV_VERIFY_CHANNEL_ID as string) - : (process.env.DISCORD_PROD_VERIFY_CHANNEL_ID as string) + : (process.env.DISCORD_PROD_VERIFY_CHANNEL_ID as string), ); if (!channel || !channel.isTextBased()) { @@ -198,9 +208,9 @@ app.post("/api/checkDiscordVerification", async (h) => { return h.json({ success: false }); } - const { discordRole: userGroupRoleName } = (c.groups as Record)[ - Object.keys(c.groups)[user.group] - ]; + const { discordRole: userGroupRoleName } = ( + c.groups as Record + )[Object.keys(c.groups)[user.group]]; const guild = client.guilds.cache.get(verification.guild); if (!guild) { @@ -208,15 +218,19 @@ app.post("/api/checkDiscordVerification", async (h) => { return h.json({ success: false }); } - const role = guild.roles.cache.find((role) => role.name === c.botParticipantRole); - const userGroupRole = guild.roles.cache.find((role) => role.name === userGroupRoleName); + const role = guild.roles.cache.find( + (role) => role.name === c.botParticipantRole, + ); + const userGroupRole = guild.roles.cache.find( + (role) => role.name === userGroupRoleName, + ); if (!role || !userGroupRole) { console.log( "failed cause could not find a role, was looking for group " + user.group + " called " + - userGroupRoleName + userGroupRoleName, ); return h.json({ success: false }); } diff --git a/apps/bot/commands/ping.ts b/apps/bot/commands/ping.ts index 46d3ee9d..0c07504f 100644 --- a/apps/bot/commands/ping.ts +++ b/apps/bot/commands/ping.ts @@ -1,6 +1,8 @@ import { SlashCommandBuilder, type CommandInteraction } from "discord.js"; -export const data = new SlashCommandBuilder().setName("ping").setDescription("Replies with Pong!"); +export const data = new SlashCommandBuilder() + .setName("ping") + .setDescription("Replies with Pong!"); export const execute = async (interaction: CommandInteraction) => { await interaction.reply("Pong!"); diff --git a/apps/bot/deploy-commands.ts b/apps/bot/deploy-commands.ts index 8018d38c..32d67e8a 100644 --- a/apps/bot/deploy-commands.ts +++ b/apps/bot/deploy-commands.ts @@ -5,7 +5,9 @@ import path from "node:path"; const args = process.execArgv; if (!args.includes("--prod") && !args.includes("--dev")) { - console.error("You must specify either --prod or --dev when deploying commands."); + console.error( + "You must specify either --prod or --dev when deploying commands.", + ); process.exit(1); } @@ -14,7 +16,9 @@ const runType: "dev" | "prod" = args.includes("--prod") ? "prod" : "dev"; const commands = []; // Grab all the command folders from the commands directory you created earlier const commandsPath = path.join(__dirname, "commands"); -const commandFiles = fs.readdirSync(commandsPath).filter((file) => file.endsWith(".ts")); +const commandFiles = fs + .readdirSync(commandsPath) + .filter((file) => file.endsWith(".ts")); // Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment for (const file of commandFiles) { const filePath = path.join(commandsPath, file); @@ -23,7 +27,7 @@ for (const file of commandFiles) { commands.push(command.data.toJSON()); } else { console.log( - `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.` + `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`, ); } } @@ -34,34 +38,45 @@ const rest = new REST().setToken(process.env.DISCORD_SECRET_TOKEN as string); // and deploy your commands! (async () => { try { - console.log(`Started refreshing ${commands.length} application (/) commands.`); + console.log( + `Started refreshing ${commands.length} application (/) commands.`, + ); // The put method is used to fully refresh all commands in the guild with the current set if (runType === "dev") { const data = await rest.put( Routes.applicationGuildCommands( process.env.DISCORD_CLIENT_ID as string, - process.env.DISCORD_DEV_SERVER_ID as string + process.env.DISCORD_DEV_SERVER_ID as string, ), { body: commands, - } + }, + ); + console.log( + `Successfully reloaded ${(data as any).length} application (/) commands.`, ); - console.log(`Successfully reloaded ${(data as any).length} application (/) commands.`); } else { const data = await rest.put( Routes.applicationGuildCommands( process.env.DISCORD_CLIENT_ID as string, - process.env.DISCORD_PROD_SERVER_ID as string + process.env.DISCORD_PROD_SERVER_ID as string, ), { body: commands, - } + }, + ); + await rest.put( + Routes.applicationCommands( + process.env.DISCORD_CLIENT_ID as string, + ), + { + body: commands, + }, + ); + console.log( + `Successfully reloaded ${(data as any).length} application (/) commands.`, ); - await rest.put(Routes.applicationCommands(process.env.DISCORD_CLIENT_ID as string), { - body: commands, - }); - console.log(`Successfully reloaded ${(data as any).length} application (/) commands.`); } } catch (error) { // And of course, make sure you catch and log any errors! diff --git a/apps/web/components.json b/apps/web/components.json index 00c912a9..037d4f42 100644 --- a/apps/web/components.json +++ b/apps/web/components.json @@ -1,15 +1,15 @@ { - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": true, - "tailwind": { - "config": "tailwind.config.js", - "css": "src/app/globals.css", - "baseColor": "zinc", - "cssVariables": true - }, - "aliases": { - "components": "@/components/shadcn", - "utils": "@/lib/utils/client/cn" - } -} \ No newline at end of file + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/app/globals.css", + "baseColor": "zinc", + "cssVariables": true + }, + "aliases": { + "components": "@/components/shadcn", + "utils": "@/lib/utils/client/cn" + } +} diff --git a/apps/web/postcss.config.js b/apps/web/postcss.config.js index 33ad091d..e873f1a4 100644 --- a/apps/web/postcss.config.js +++ b/apps/web/postcss.config.js @@ -1,6 +1,6 @@ module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/apps/web/src/actions/admin/modify-nav-item.ts b/apps/web/src/actions/admin/modify-nav-item.ts index 5baa0333..b52e253b 100644 --- a/apps/web/src/actions/admin/modify-nav-item.ts +++ b/apps/web/src/actions/admin/modify-nav-item.ts @@ -13,28 +13,40 @@ const metadataSchema = z.object({ // Maybe a better way to do this for revalidation? Who knows. const navAdminPage = "/admin/toggles/landing"; -export const setItem = adminAction(metadataSchema, async ({ name, url }, { user, userId }) => { - await kv.sadd("config:navitemslist", encodeURIComponent(name)); - await kv.hset(`config:navitems:${encodeURIComponent(name)}`, { url, name, enabled: true }); - revalidatePath(navAdminPage); - return { success: true }; -}); +export const setItem = adminAction( + metadataSchema, + async ({ name, url }, { user, userId }) => { + await kv.sadd("config:navitemslist", encodeURIComponent(name)); + await kv.hset(`config:navitems:${encodeURIComponent(name)}`, { + url, + name, + enabled: true, + }); + revalidatePath(navAdminPage); + return { success: true }; + }, +); -export const removeItem = adminAction(z.string(), async (name, { user, userId }) => { - const pipe = kv.pipeline(); - pipe.srem("config:navitemslist", encodeURIComponent(name)); - pipe.del(`config:navitems:${encodeURIComponent(name)}`); - await pipe.exec(); - // await new Promise((resolve) => setTimeout(resolve, 1500)); - revalidatePath(navAdminPage); - return { success: true }; -}); +export const removeItem = adminAction( + z.string(), + async (name, { user, userId }) => { + const pipe = kv.pipeline(); + pipe.srem("config:navitemslist", encodeURIComponent(name)); + pipe.del(`config:navitems:${encodeURIComponent(name)}`); + await pipe.exec(); + // await new Promise((resolve) => setTimeout(resolve, 1500)); + revalidatePath(navAdminPage); + return { success: true }; + }, +); export const toggleItem = adminAction( z.object({ name: z.string(), statusToSet: z.boolean() }), async ({ name, statusToSet }, { user, userId }) => { - await kv.hset(`config:navitems:${encodeURIComponent(name)}`, { enabled: statusToSet }); + await kv.hset(`config:navitems:${encodeURIComponent(name)}`, { + enabled: statusToSet, + }); revalidatePath(navAdminPage); return { success: true, itemStatus: statusToSet }; - } + }, ); diff --git a/apps/web/src/actions/admin/registration-actions.ts b/apps/web/src/actions/admin/registration-actions.ts index e10ac735..9388bbba 100644 --- a/apps/web/src/actions/admin/registration-actions.ts +++ b/apps/web/src/actions/admin/registration-actions.ts @@ -15,7 +15,7 @@ export const toggleRegistrationEnabled = adminAction( await kv.set("config:registration:registrationEnabled", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; - } + }, ); export const toggleRegistrationMessageEnabled = adminAction( @@ -24,7 +24,7 @@ export const toggleRegistrationMessageEnabled = adminAction( await kv.set("config:registration:registrationMessageEnabled", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; - } + }, ); export const toggleSecretRegistrationEnabled = adminAction( @@ -33,7 +33,7 @@ export const toggleSecretRegistrationEnabled = adminAction( await kv.set("config:registration:secretRegistrationEnabled", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; - } + }, ); export const toggleRSVPs = adminAction( @@ -42,5 +42,5 @@ export const toggleRSVPs = adminAction( await kv.set("config:registration:allowRSVPs", enabled); revalidatePath("/admin/toggles/registration"); return { success: true, statusSet: enabled }; - } + }, ); diff --git a/apps/web/src/actions/admin/scanner-admin-actions.ts b/apps/web/src/actions/admin/scanner-admin-actions.ts index 1075fde8..16389437 100644 --- a/apps/web/src/actions/admin/scanner-admin-actions.ts +++ b/apps/web/src/actions/admin/scanner-admin-actions.ts @@ -6,60 +6,62 @@ import { db } from "db"; import { scans, users } from "db/schema"; import { eq, and } from "db/drizzle"; export const createScan = adminAction( - z.object({ - eventID: z.number(), - userID: z.string(), - creationTime: z.date(), - countToSet: z.number(), - alreadyExists: z.boolean(), - }), - async ( - { eventID, userID, creationTime, countToSet, alreadyExists }, - { user, userId } - ) => { - if (alreadyExists) { - await db - .update(scans) - .set({ count: countToSet, updatedAt: creationTime }) - .where(and(eq(scans.eventID, eventID), eq(scans.userID, userID))); - } else { - await db.insert(scans).values({ - userID: userID, - updatedAt: creationTime, - count: 1, - eventID: eventID, - }); - } - return { success: true }; - } + z.object({ + eventID: z.number(), + userID: z.string(), + creationTime: z.date(), + countToSet: z.number(), + alreadyExists: z.boolean(), + }), + async ( + { eventID, userID, creationTime, countToSet, alreadyExists }, + { user, userId }, + ) => { + if (alreadyExists) { + await db + .update(scans) + .set({ count: countToSet, updatedAt: creationTime }) + .where( + and(eq(scans.eventID, eventID), eq(scans.userID, userID)), + ); + } else { + await db.insert(scans).values({ + userID: userID, + updatedAt: creationTime, + count: 1, + eventID: eventID, + }); + } + return { success: true }; + }, ); export const getScan = adminAction( - z.object({ eventID: z.number(), userID: z.string() }), - async ({ eventID, userID }, { user, userId: adminUserID }) => { - const scan = await db.query.scans.findFirst({ - where: and(eq(scans.eventID, eventID), eq(scans.userID, userID)), - }); - return scan; - } + z.object({ eventID: z.number(), userID: z.string() }), + async ({ eventID, userID }, { user, userId: adminUserID }) => { + const scan = await db.query.scans.findFirst({ + where: and(eq(scans.eventID, eventID), eq(scans.userID, userID)), + }); + return scan; + }, ); export const 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)); - } + 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)); + }, ); diff --git a/apps/web/src/actions/admin/user-actions.ts b/apps/web/src/actions/admin/user-actions.ts index caed729a..a347d92b 100644 --- a/apps/web/src/actions/admin/user-actions.ts +++ b/apps/web/src/actions/admin/user-actions.ts @@ -9,22 +9,22 @@ import { eq } from "db/drizzle"; import { revalidatePath } from "next/cache"; export const updateRole = adminAction( - z.object({ - userIDToUpdate: z.string(), - roleToSet: z.enum(perms), - }), - async ({ userIDToUpdate, roleToSet }, { user, userId }) => { - if ( - user.role !== "super_admin" && - (roleToSet === "super_admin" || roleToSet === "admin") - ) { - throw new Error("You are not allowed to do this"); - } - await db - .update(users) - .set({ role: roleToSet }) - .where(eq(users.clerkID, userIDToUpdate)); - revalidatePath(`/admin/users/${userIDToUpdate}`); - return { success: true }; - } + z.object({ + userIDToUpdate: z.string(), + roleToSet: z.enum(perms), + }), + async ({ userIDToUpdate, roleToSet }, { user, userId }) => { + if ( + user.role !== "super_admin" && + (roleToSet === "super_admin" || roleToSet === "admin") + ) { + throw new Error("You are not allowed to do this"); + } + await db + .update(users) + .set({ role: roleToSet }) + .where(eq(users.clerkID, userIDToUpdate)); + revalidatePath(`/admin/users/${userIDToUpdate}`); + return { success: true }; + }, ); diff --git a/apps/web/src/actions/discord-verify.ts b/apps/web/src/actions/discord-verify.ts index d8f481ed..e29a9353 100644 --- a/apps/web/src/actions/discord-verify.ts +++ b/apps/web/src/actions/discord-verify.ts @@ -13,7 +13,10 @@ export const confirmVerifyDiscord = authenticatedAction( }), async ({ code }, { userId }) => { const verification = await db.query.discordVerification.findFirst({ - where: and(eq(discordVerification.code, code), eq(discordVerification.status, "pending")), + where: and( + eq(discordVerification.code, code), + eq(discordVerification.status, "pending"), + ), }); if (!verification) { return { @@ -29,14 +32,16 @@ export const confirmVerifyDiscord = authenticatedAction( .where(eq(discordVerification.code, code)); const res = await fetch( - env.BOT_API_URL + "/api/checkDiscordVerification?access=" + env.INTERNAL_AUTH_KEY, + env.BOT_API_URL + + "/api/checkDiscordVerification?access=" + + env.INTERNAL_AUTH_KEY, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ code }), - } + }, ); let resJson = await res.json(); console.log(resJson); @@ -44,5 +49,5 @@ export const confirmVerifyDiscord = authenticatedAction( return { success: true, }; - } + }, ); diff --git a/apps/web/src/actions/rsvp.ts b/apps/web/src/actions/rsvp.ts index ff935473..1e13fdf7 100644 --- a/apps/web/src/actions/rsvp.ts +++ b/apps/web/src/actions/rsvp.ts @@ -6,9 +6,17 @@ import { db } from "db"; import { eq } from "db/drizzle"; import { users } from "db/schema"; -export const rsvpMyself = authenticatedAction(z.any(), async (_, { userId }) => { - const user = await db.query.users.findFirst({ where: eq(users.clerkID, userId) }); - if (!user) throw new Error("User not found"); - await db.update(users).set({ rsvp: true }).where(eq(users.clerkID, userId)); - return { success: true }; -}); +export const rsvpMyself = authenticatedAction( + z.any(), + async (_, { userId }) => { + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), + }); + if (!user) throw new Error("User not found"); + await db + .update(users) + .set({ rsvp: true }) + .where(eq(users.clerkID, userId)); + return { success: true }; + }, +); diff --git a/apps/web/src/actions/teams.ts b/apps/web/src/actions/teams.ts index 29d968e7..dff785d0 100644 --- a/apps/web/src/actions/teams.ts +++ b/apps/web/src/actions/teams.ts @@ -10,67 +10,77 @@ 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, - }, - }); - - if (!user) { - throw new Error("User not found"); - } - - if (user.team === null || user.team === undefined) { - revalidatePath("/dash/team"); - return { - success: false, - message: "User is not on a team", - }; - } - - const result = await db.transaction(async (tx) => { - await tx.update(users).set({ teamID: null }).where(eq(users.clerkID, userId)); - 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. +export const leaveTeam = authenticatedAction( + z.null(), + async (_, { userId }) => { + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), with: { - members: true, + team: true, }, }); - if (!team) { - revalidatePath("/dash/team"); - return { - success: false, - message: "Team not found.", - }; + if (!user) { + throw new Error("User not found"); } - if (team.members.length < 1) { - await tx.delete(teams).where(eq(teams.id, team.id)); - await tx.delete(invites).where(eq(invites.teamID, team.id)); + if (user.team === null || user.team === undefined) { revalidatePath("/dash/team"); return { - success: true, - message: "Team has been left. Team has been deleted since it has no members.", + success: false, + message: "User is not on a team", }; } - if (team.ownerID == userId) { - await tx.update(teams).set({ ownerID: team.members[0].clerkID }).where(eq(teams.id, team.id)); + const result = await db.transaction(async (tx) => { + await tx + .update(users) + .set({ teamID: null }) + .where(eq(users.clerkID, userId)); + 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. + with: { + members: true, + }, + }); + + if (!team) { + revalidatePath("/dash/team"); + return { + success: false, + message: "Team not found.", + }; + } + + if (team.members.length < 1) { + await tx.delete(teams).where(eq(teams.id, team.id)); + await tx.delete(invites).where(eq(invites.teamID, team.id)); + revalidatePath("/dash/team"); + return { + success: true, + message: + "Team has been left. Team has been deleted since it has no members.", + }; + } + + if (team.ownerID == userId) { + await tx + .update(teams) + .set({ ownerID: team.members[0].clerkID }) + .where(eq(teams.id, team.id)); + revalidatePath("/dash/team"); + return { + success: true, + message: `Team has been left. Ownership has been transferred to ${team.members[0].firstName} ${team.members[0].lastName}.`, + }; + } 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.", }; - } - revalidatePath("/dash/team"); - return { - success: true, - message: "Team has been left.", - }; - }); + }); - return result; -}); + return result; + }, +); diff --git a/apps/web/src/actions/user-profile-mod.ts b/apps/web/src/actions/user-profile-mod.ts index 682bcf51..9c73942c 100644 --- a/apps/web/src/actions/user-profile-mod.ts +++ b/apps/web/src/actions/user-profile-mod.ts @@ -11,56 +11,60 @@ import { revalidatePath } from "next/cache"; // TODO: Add skill updating export const modifyRegistrationData = authenticatedAction( - z.object({ - bio: z.string().max(500), - skills: z.string().max(100), - }), - async ({ bio, skills }, { userId }) => { - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - }); - if (!user) throw new Error("User not found"); - await db - .update(profileData) - .set({ bio }) - .where(eq(profileData.hackerTag, user.hackerTag)); - return { success: true, newbio: bio }; - } + z.object({ + bio: z.string().max(500), + skills: z.string().max(100), + }), + async ({ bio, skills }, { userId }) => { + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), + }); + if (!user) throw new Error("User not found"); + await db + .update(profileData) + .set({ bio }) + .where(eq(profileData.hackerTag, user.hackerTag)); + return { success: true, newbio: bio }; + }, ); export const modifyAccountSettings = authenticatedAction( - z.object({ - firstName: z.string().min(1).max(50), - lastName: z.string().min(1).max(50), - }), - async ({ firstName, lastName }, { userId }) => { - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - }); - if (!user) throw new Error("User not found"); - await db - .update(users) - .set({ firstName, lastName }) - .where(eq(users.clerkID, userId)); - return { success: true, newFirstName: firstName, newLastName: lastName }; - } + z.object({ + firstName: z.string().min(1).max(50), + lastName: z.string().min(1).max(50), + }), + async ({ firstName, lastName }, { userId }) => { + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), + }); + if (!user) throw new Error("User not found"); + await db + .update(users) + .set({ firstName, lastName }) + .where(eq(users.clerkID, userId)); + return { + success: true, + newFirstName: firstName, + newLastName: lastName, + }; + }, ); export const updateProfileImage = authenticatedAction( - z.object({ fileBase64: z.string(), fileName: z.string() }), - async ({ fileBase64, fileName }, { userId }) => { - const image = await decodeBase64AsFile(fileBase64, fileName); - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - }); - if (!user) throw new Error("User not found"); + z.object({ fileBase64: z.string(), fileName: z.string() }), + async ({ fileBase64, fileName }, { userId }) => { + const image = await decodeBase64AsFile(fileBase64, fileName); + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), + }); + if (!user) throw new Error("User not found"); - const blobUpload = await put(image.name, image, { access: "public" }); - await db - .update(profileData) - .set({ profilePhoto: blobUpload.url }) - .where(eq(profileData.hackerTag, user.hackerTag)); - revalidatePath("/settings/profile"); - return { success: true }; - } + const blobUpload = await put(image.name, image, { access: "public" }); + await db + .update(profileData) + .set({ profilePhoto: blobUpload.url }) + .where(eq(profileData.hackerTag, user.hackerTag)); + revalidatePath("/settings/profile"); + return { success: true }; + }, ); diff --git a/apps/web/src/app/admin/check-in/page.tsx b/apps/web/src/app/admin/check-in/page.tsx index 3d01b6f4..2bbdafea 100644 --- a/apps/web/src/app/admin/check-in/page.tsx +++ b/apps/web/src/app/admin/check-in/page.tsx @@ -5,72 +5,82 @@ import { eq, and } from "db/drizzle"; import { events, users, scans } from "db/schema"; export default async function Page({ - searchParams, + searchParams, }: { - searchParams: { [key: string]: string | undefined }; + searchParams: { [key: string]: string | undefined }; }) { - // TODO: maybe move event existant check into a layout so it holds state? + // TODO: maybe move event existant check into a layout so it holds state? - // if (!params || !params.id || isNaN(parseInt(params.id))) { - // return ( - // - // ); - // } + // if (!params || !params.id || isNaN(parseInt(params.id))) { + // return ( + // + // ); + // } - // const event = await db.query.events.findFirst({ - // where: eq(events.id, parseInt(params.id)), - // }); + // const event = await db.query.events.findFirst({ + // where: eq(events.id, parseInt(params.id)), + // }); - // if (!event) { - // return ( - // - // ); - // } + // 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]; - } - }); - - 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]; + } + }, + ); - // Fall through case - return ( -
- -
- ); + return ( +
+ +
+ ); + } + + // Fall through case + return ( +
+ +
+ ); } export const runtime = "edge"; diff --git a/apps/web/src/app/admin/events/new/page.tsx b/apps/web/src/app/admin/events/new/page.tsx index 1170cbc6..6a11aa99 100644 --- a/apps/web/src/app/admin/events/new/page.tsx +++ b/apps/web/src/app/admin/events/new/page.tsx @@ -8,7 +8,7 @@ export default function Page() {

New Event

-
+
diff --git a/apps/web/src/app/admin/events/page.tsx b/apps/web/src/app/admin/events/page.tsx index 6f343beb..2a9d0ae1 100644 --- a/apps/web/src/app/admin/events/page.tsx +++ b/apps/web/src/app/admin/events/page.tsx @@ -6,29 +6,31 @@ import { PlusCircle } from "lucide-react"; import Link from "next/link"; export default async function Page() { - const events = await db.query.events.findMany(); + const events = await db.query.events.findMany(); - return ( -
-
-
-
-

Events

-

- {events.length} Event{events.length != 1 ? "s" : ""} -

-
-
-
- - - -
-
- -
- ); + return ( +
+
+
+
+

+ Events +

+

+ {events.length} Event{events.length != 1 ? "s" : ""} +

+
+
+
+ + + +
+
+ +
+ ); } diff --git a/apps/web/src/app/admin/layout.tsx b/apps/web/src/app/admin/layout.tsx index ec77dea6..9cae4ee7 100644 --- a/apps/web/src/app/admin/layout.tsx +++ b/apps/web/src/app/admin/layout.tsx @@ -14,74 +14,82 @@ import ClientToast from "@/components/shared/ClientToast"; import { redirect } from "next/navigation"; import NavBarLinksGrouper from "@/components/shared/NavBarLinksGrouper"; - interface AdminLayoutProps { - children: React.ReactNode; + children: React.ReactNode; } export default async function AdminLayout({ children }: AdminLayoutProps) { - const { userId } = auth(); + const { userId } = auth(); - if (!userId) { - return redirect("/sign-in"); - } + if (!userId) { + return redirect("/sign-in"); + } - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - }); + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), + }); - if (!user || (user.role !== "admin" && user.role !== "super_admin")) { - console.log("Denying admin access to user", user); - return ( - - ); - } + if (!user || (user.role !== "admin" && user.role !== "super_admin")) { + console.log("Denying admin access to user", user); + return ( + + ); + } - return ( - <> - -
-
- {c.hackathonName -
-

Admin

-
-
- - - - - - - - - - -
-
-
-
- {Object.entries(c.dashPaths.admin).map(([name, path]) => ( - - ))} -
- Loading...

}>{children}
- - ); + return ( + <> + +
+
+ {c.hackathonName +
+

Admin

+
+
+ + + + + + + + + + +
+
+
+
+ {Object.entries(c.dashPaths.admin).map(([name, path]) => ( + + ))} +
+ Loading...

}>{children}
+ + ); } export const runtime = "edge"; diff --git a/apps/web/src/app/admin/page.tsx b/apps/web/src/app/admin/page.tsx index 76f2a223..29b408ff 100644 --- a/apps/web/src/app/admin/page.tsx +++ b/apps/web/src/app/admin/page.tsx @@ -9,7 +9,7 @@ import { import { db } from "db"; import { eq, desc } from "db/drizzle"; import { users } from "db/schema"; -import { Users, UserCheck, User2, TimerReset,MailCheck } from "lucide-react"; +import { Users, UserCheck, User2, TimerReset, MailCheck } from "lucide-react"; import type { userType } from "@/lib/utils/shared/types"; import { unstable_cache } from "next/cache"; import { env } from "@/env.mjs"; @@ -30,34 +30,46 @@ export default async function Page() { orderBy: desc(users.createdAt), }); - if (!adminUser || (adminUser.role !== "admin" && adminUser.role !== "super_admin")) { + if ( + !adminUser || + (adminUser.role !== "admin" && adminUser.role !== "super_admin") + ) { return notFound(); } const allUsers = await getUsers(); - const { rsvpCount, checkinCount, recentSignupCount } = getRecentRegistrationData(allUsers); + const { rsvpCount, checkinCount, recentSignupCount } = + getRecentRegistrationData(allUsers); return ( -
+
-

Welcome,

-

{adminUser.firstName}

+

Welcome,

+

+ {adminUser.firstName} +

- Registrations + + Registrations + -
{allUsers.length}
+
+ {allUsers.length} +
{/*

+20.1% from last month

*/}
- Teams + + Teams + @@ -67,8 +79,10 @@ export default async function Page() { - RSVPs - + + RSVPs + +
{rsvpCount}
@@ -77,8 +91,10 @@ export default async function Page() {
- Check-ins - + + Check-ins + +
{checkinCount}
@@ -90,10 +106,16 @@ export default async function Page() {
- Registrations{" "} + + Registrations + {" "} - {Object.values(recentSignupCount).reduce((a, b) => a + b, 0)} new registrations have - occurred in the past 7 days. + {Object.values(recentSignupCount).reduce( + (a, b) => a + b, + 0, + )}{" "} + new registrations have occurred in the past 7 + days.
@@ -106,7 +128,9 @@ export default async function Page() {
- Recent Registrations{" "} + + Recent Registrations + {" "}
@@ -132,14 +156,12 @@ function getRecentRegistrationData(users: userType[]) { // Format the date as YYYY-MM-DD const dateString = date.toISOString().split("T")[0]; - + // Assign a default value, e.g., 0 recentSignupCount[dateString] = 0; } for (const user of users) { - - if (user.rsvp) rsvpCount++; if (user.checkedIn) checkinCount++; diff --git a/apps/web/src/app/admin/scanner/[id]/page.tsx b/apps/web/src/app/admin/scanner/[id]/page.tsx index f7de2aa7..b48b2b49 100644 --- a/apps/web/src/app/admin/scanner/[id]/page.tsx +++ b/apps/web/src/app/admin/scanner/[id]/page.tsx @@ -15,7 +15,10 @@ export default async function Page({ if (!params || !params.id || isNaN(parseInt(params.id))) { return ( - + ); } @@ -25,7 +28,10 @@ export default async function Page({ if (!event) { return ( - + ); } @@ -38,7 +44,10 @@ export default async function Page({ return [null, null]; } const scan = await tx.query.scans.findFirst({ - where: and(eq(scans.eventID, event.id), eq(scans.userID, scanUser.clerkID)), + where: and( + eq(scans.eventID, event.id), + eq(scans.userID, scanUser.clerkID), + ), }); if (scan) { return [scan, scanUser]; @@ -48,14 +57,24 @@ export default async function Page({ }); return (
- +
); } return (
- +
); } diff --git a/apps/web/src/app/admin/toggles/landing/page.tsx b/apps/web/src/app/admin/toggles/landing/page.tsx index 642d19fe..3cc22a64 100644 --- a/apps/web/src/app/admin/toggles/landing/page.tsx +++ b/apps/web/src/app/admin/toggles/landing/page.tsx @@ -1,4 +1,7 @@ -import { NavItemsManager, NavItemDialog } from "@/components/admin/toggles/NavItemsManager"; +import { + NavItemsManager, + NavItemDialog, +} from "@/components/admin/toggles/NavItemsManager"; import { getAllNavItems } from "@/lib/utils/server/redis"; export default async function Page() { @@ -6,12 +9,18 @@ export default async function Page() { return (
-

Navbar Items

+

+ Navbar Items +

- a.name.localeCompare(b.name))} /> + + a.name.localeCompare(b.name), + )} + />
); } diff --git a/apps/web/src/app/admin/toggles/layout.tsx b/apps/web/src/app/admin/toggles/layout.tsx index eebd04f1..b56bb32d 100644 --- a/apps/web/src/app/admin/toggles/layout.tsx +++ b/apps/web/src/app/admin/toggles/layout.tsx @@ -6,13 +6,19 @@ interface ToggleLayoutProps { export default function Layout({ children }: ToggleLayoutProps) { return ( -
+
- - + +
{children}
diff --git a/apps/web/src/app/admin/toggles/page.tsx b/apps/web/src/app/admin/toggles/page.tsx index c4d4799a..be7a63f3 100644 --- a/apps/web/src/app/admin/toggles/page.tsx +++ b/apps/web/src/app/admin/toggles/page.tsx @@ -1,10 +1,11 @@ export default async function Page() { return ( -
-

Toggles

+
+

Toggles

- Toggles allow you to control various dynamic options on the website. If you don't see an - option here, chances are it can be changed in the hackkit.config.ts file or via + Toggles allow you to control various dynamic options on the + website. If you don't see an option here, chances are it can be + changed in the hackkit.config.ts file or via enviroment variables. Visit the HackKit docs to learn more!

diff --git a/apps/web/src/app/admin/toggles/registration/page.tsx b/apps/web/src/app/admin/toggles/registration/page.tsx index f1d9faa4..3825236b 100644 --- a/apps/web/src/app/admin/toggles/registration/page.tsx +++ b/apps/web/src/app/admin/toggles/registration/page.tsx @@ -8,27 +8,36 @@ export default async function Page() { pipe.get("config:registration:secretRegistrationEnabled"); // const result = await pipe.exec(); - const [defaultRegistrationEnabled, defaultSecretRegistrationEnabled, defaultRSVPsEnabled]: ( - | string - | null - )[] = await kv.mget( + const [ + defaultRegistrationEnabled, + defaultSecretRegistrationEnabled, + defaultRSVPsEnabled, + ]: (string | null)[] = await kv.mget( "config:registration:registrationEnabled", "config:registration:secretRegistrationEnabled", - "config:registration:allowRSVPs" + "config:registration:allowRSVPs", ); return (
-

Registration & Sign-in

+

+ Registration & Sign-in +

); diff --git a/apps/web/src/app/admin/users/[slug]/page.tsx b/apps/web/src/app/admin/users/[slug]/page.tsx index c28b6699..c2bd74b1 100644 --- a/apps/web/src/app/admin/users/[slug]/page.tsx +++ b/apps/web/src/app/admin/users/[slug]/page.tsx @@ -43,11 +43,11 @@ export default async function Page({ params }: { params: { slug: string } }) { } return ( -
-
+
+
-

+

User Info

@@ -67,9 +67,9 @@ export default async function Page({ params }: { params: { slug: string } }) { />
-
-
-
+
+
+
{`Profile
-

+

{user.firstName} {user.lastName}

-

@{user.hackerTag}

+

+ @{user.hackerTag} +

{/*

{team.bio}

*/} -
+
- Joined {user.createdAt.toDateString().split(" ").slice(1).join(" ")} + Joined{" "} + {user.createdAt + .toDateString() + .split(" ") + .slice(1) + .join(" ")}
diff --git a/apps/web/src/app/admin/users/page.tsx b/apps/web/src/app/admin/users/page.tsx index 039d717f..b1b17c23 100644 --- a/apps/web/src/app/admin/users/page.tsx +++ b/apps/web/src/app/admin/users/page.tsx @@ -1,4 +1,4 @@ -import { db,ilike,or,and,eq } from "db"; +import { db, ilike, or, and, eq } from "db"; import { DataTable } from "@/components/admin/users/UserDataTable"; import { columns } from "@/components/admin/users/UserColumns"; import { Button } from "@/components/shadcn/ui/button"; @@ -9,76 +9,81 @@ import Filters from "../../../components/admin/users/Filters"; import { users } from "db/schema"; import { parseCheckBoxParams } from "@/lib/utils/shared/pageParams"; -export default async function Page({searchParams}:{searchParams:{[key:string]:string |undefined}}) { - // COME BACK AND CHANGE - const maxPerPage = 30; +export default async function Page({ + searchParams, +}: { + searchParams: { [key: string]: string | undefined }; +}) { + // COME BACK AND CHANGE + const maxPerPage = 30; - let page = +(searchParams["page"] ?? "1"); - let user = searchParams["user"] ?? ""; - const checkedBoxes = searchParams["checkedBoxes"] ?? ""; - + let page = +(searchParams["page"] ?? "1"); + let user = searchParams["user"] ?? ""; + const checkedBoxes = searchParams["checkedBoxes"] ?? ""; - console.log(checkedBoxes); + console.log(checkedBoxes); - const start = maxPerPage * (page - 1); - const end = maxPerPage + start; + const start = maxPerPage * (page - 1); + const end = maxPerPage + start; + // Might want to work with cache in prod to see if this will be plausible to do + const userData = await db.query.users.findMany({ + with: { + registrationData: true, + profileData: true, + }, + where: and( + or( + ilike(users.firstName, `%${user}%`), + ilike(users.lastName, `%${user}%`), + ), + ), + }); -// Might want to work with cache in prod to see if this will be plausible to do - const userData = await db.query.users.findMany({ - with: { - registrationData: true, - profileData: true, - }, - where: and( - or( - ilike(users.firstName, `%${user}%`), - ilike(users.lastName, `%${user}%`) - ), - ), - }); - - - return ( -
-
-
-
-

Users

-

- Total Users: {userData.length} -

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

No Results :(

-
- )} - {/* */} -
- -
- ); + return ( +
+
+
+
+

+ Users +

+

+ Total Users: {userData.length} +

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

No Results :(

+
+ )} + {/* */} +
+ +
+ ); } export const runtime = "edge"; 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 ebac88ee..9ca5ec1e 100644 --- a/apps/web/src/app/api/admin/events/create/route.ts +++ b/apps/web/src/app/api/admin/events/create/route.ts @@ -18,7 +18,10 @@ export async function POST(req: Request) { where: eq(users.clerkID, userId), }); - if (!reqUserRecord || (reqUserRecord.role !== "super_admin" && reqUserRecord.role !== "admin")) { + if ( + !reqUserRecord || + (reqUserRecord.role !== "super_admin" && reqUserRecord.role !== "admin") + ) { return new Response("Unauthorized", { status: 401 }); } diff --git a/apps/web/src/app/api/admin/export/route.ts b/apps/web/src/app/api/admin/export/route.ts index 25e04150..404a1fdb 100644 --- a/apps/web/src/app/api/admin/export/route.ts +++ b/apps/web/src/app/api/admin/export/route.ts @@ -7,7 +7,8 @@ function escape(value: any) { if (value === null) return "None"; // convert to string if it's not already - const stringValue = typeof value !== "string" ? JSON.stringify(value) : value; + const stringValue = + typeof value !== "string" ? JSON.stringify(value) : value; // escape double quotes and enclose in quotes if it contains comma, newline or double quote if (/[",\n]/.test(stringValue)) { @@ -23,7 +24,9 @@ function jsonToCSV(json: any[]): string { } const header = Object.keys(json[0]); - let csv = json.map((row) => header.map((fieldName) => escape(row[fieldName])).join(",")); + let csv = json.map((row) => + header.map((fieldName) => escape(row[fieldName])).join(","), + ); csv.unshift(header.join(",")); return csv.join("\r\n"); @@ -38,7 +41,10 @@ export async function GET() { where: eq(users.clerkID, userId), }); - if (!reqUserRecord || (reqUserRecord.role !== "super_admin" && reqUserRecord.role !== "admin")) { + if ( + !reqUserRecord || + (reqUserRecord.role !== "super_admin" && reqUserRecord.role !== "admin") + ) { return new Response("Unauthorized", { status: 401 }); } @@ -51,7 +57,11 @@ export async function GET() { const columed = 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 }; + let toRet: any = { + ...user, + ...user.registrationData, + ...user.profileData, + }; delete toRet.registrationData; delete toRet.profileData; return toRet; diff --git a/apps/web/src/app/api/registration/create/route.ts b/apps/web/src/app/api/registration/create/route.ts index 2bcda40b..048c8951 100644 --- a/apps/web/src/app/api/registration/create/route.ts +++ b/apps/web/src/app/api/registration/create/route.ts @@ -10,9 +10,9 @@ import { sendEmail } from "@/lib/utils/server/ses"; export async function POST(req: Request) { const rawBody = await req.json(); - const parsedBody = RegisterFormValidator.merge(z.object({ resume: z.string().url() })).safeParse( - rawBody - ); + const parsedBody = RegisterFormValidator.merge( + z.object({ resume: z.string().url() }), + ).safeParse(rawBody); if (!parsedBody.success) { return NextResponse.json( @@ -20,7 +20,7 @@ export async function POST(req: Request) { success: false, message: "Malformed request body.", }, - { status: 400 } + { status: 400 }, ); } @@ -34,7 +34,7 @@ export async function POST(req: Request) { success: false, message: "You must be logged in to register.", }, - { status: 401 } + { status: 401 }, ); } @@ -45,7 +45,7 @@ export async function POST(req: Request) { success: false, message: "You are already registered.", }, - { status: 400 } + { status: 400 }, ); } @@ -61,7 +61,7 @@ export async function POST(req: Request) { success: false, message: "You are already registered.", }, - { status: 400 } + { status: 400 }, ); } @@ -83,7 +83,8 @@ export async function POST(req: Request) { if (!body.acceptsMLHCodeOfConduct || !body.shareDataWithMLH) { return NextResponse.json({ success: false, - message: "You must accept the MLH Code of Conduct and Privacy Policy.", + message: + "You must accept the MLH Code of Conduct and Privacy Policy.", }); } @@ -146,7 +147,10 @@ export async function POST(req: Request) { // subject: `You are now registered for ${c.hackathonName} ${c.itteration}!`, // }); - return NextResponse.json({ success: true, message: "Successfully created registration!" }); + return NextResponse.json({ + success: true, + message: "Successfully created registration!", + }); } export const runtime = "edge"; diff --git a/apps/web/src/app/api/team/create/route.ts b/apps/web/src/app/api/team/create/route.ts index 4fc4c4f6..de723958 100644 --- a/apps/web/src/app/api/team/create/route.ts +++ b/apps/web/src/app/api/team/create/route.ts @@ -12,13 +12,16 @@ 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.users.findFirst({ + where: eq(users.clerkID, userId), + }); if (!user) return new Response("Unauthorized", { status: 401 }); if (user.teamID) { return NextResponse.json({ success: false, - message: "You are already on a team. Leave your current team to create a new one.", + message: + "You are already on a team. Leave your current team to create a new one.", }); } 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 351d8613..81b8a328 100644 --- a/apps/web/src/app/api/team/invite/accept/route.ts +++ b/apps/web/src/app/api/team/invite/accept/route.ts @@ -12,7 +12,9 @@ const inviteAcceptValidator = z.object({ teamInviteID: z.string().min(1).max(50), }); -export async function POST(req: Request): serverZodResponse { +export async function POST( + req: Request, +): serverZodResponse { const { userId } = await auth(); if (!userId) return NextResponse.json("Unauthorized", { status: 401 }); @@ -76,12 +78,20 @@ export async function POST(req: Request): serverZodResponse { +export async function POST( + req: Request, +): serverZodResponse { const { userId } = await auth(); if (!userId) return NextResponse.json("Unauthorized", { status: 401 }); const user = await db.query.users.findFirst({ @@ -21,9 +23,10 @@ export async function POST(req: Request): serverZodResponse { return NextResponse.json(jsonResponse); } catch (error) { - return NextResponse.json({ error: (error as Error).message }, { status: 400 }); + return NextResponse.json( + { error: (error as Error).message }, + { status: 400 }, + ); } } diff --git a/apps/web/src/app/api/upload/resume/register/route.ts b/apps/web/src/app/api/upload/resume/register/route.ts index 25cf0513..7fc5eb00 100644 --- a/apps/web/src/app/api/upload/resume/register/route.ts +++ b/apps/web/src/app/api/upload/resume/register/route.ts @@ -31,7 +31,10 @@ export async function POST(request: Request): Promise { return NextResponse.json(jsonResponse); } catch (error) { - return NextResponse.json({ error: (error as Error).message }, { status: 400 }); + return NextResponse.json( + { error: (error as Error).message }, + { status: 400 }, + ); } } diff --git a/apps/web/src/app/contact/page.tsx b/apps/web/src/app/contact/page.tsx index 38c14a8a..d2eebdc0 100644 --- a/apps/web/src/app/contact/page.tsx +++ b/apps/web/src/app/contact/page.tsx @@ -12,56 +12,61 @@ const oswald = Oswald({ export default function Page() { return ( <> - +
-
-
-

Contact Us

-

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

+
+

+ Contact Us +

+

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

- + -
+
-
+
diff --git a/apps/web/src/app/dash/layout.tsx b/apps/web/src/app/dash/layout.tsx index 59e030a6..6bbe6634 100644 --- a/apps/web/src/app/dash/layout.tsx +++ b/apps/web/src/app/dash/layout.tsx @@ -23,33 +23,47 @@ export default async function DashLayout({ children }: DashLayoutProps) { return ( <> -
+
- {c.hackathonName -
+ {c.hackathonName +

Dashboard

-
+
- - -
-
+
-
+
{Object.entries(c.dashPaths.dash).map(([name, path]) => ( ))} diff --git a/apps/web/src/app/dash/page.tsx b/apps/web/src/app/dash/page.tsx index 54ac21a0..b1de2cd5 100644 --- a/apps/web/src/app/dash/page.tsx +++ b/apps/web/src/app/dash/page.tsx @@ -13,7 +13,11 @@ import { createQRpayload } from "@/lib/utils/shared/qr"; // HackKit Bubbles import { Countdown } from "@/components/dash/overview/ClientBubbles"; -import { Questions, TitleBubble, QuickQR } from "@/components/dash/overview/ServerBubbles"; +import { + Questions, + TitleBubble, + QuickQR, +} from "@/components/dash/overview/ServerBubbles"; export default async function Page() { const { userId } = auth(); @@ -23,18 +27,26 @@ export default async function Page() { }); if (!user) return null; - const qrPayload = createQRpayload({ userID: userId, createdAt: new Date() }); + const qrPayload = createQRpayload({ + userID: userId, + createdAt: new Date(), + }); return ( -
+
-

Welcome,

-

{user.firstName}

+

Welcome,

+

+ {user.firstName} +

-
+
- +
diff --git a/apps/web/src/app/dash/pass/page.tsx b/apps/web/src/app/dash/pass/page.tsx index 4b558591..35ef4790 100644 --- a/apps/web/src/app/dash/pass/page.tsx +++ b/apps/web/src/app/dash/pass/page.tsx @@ -10,22 +10,21 @@ import { format } from "date-fns"; import TiltWrapper from "@/components/dash/shared/TiltWrapper"; import { createQRpayload } from "@/lib/utils/shared/qr"; import { - Drawer, - DrawerClose, - DrawerContent, - DrawerDescription, - DrawerFooter, - DrawerHeader, - DrawerTitle, - DrawerTrigger, + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, } from "@/components/shadcn/ui/drawer"; - interface EventPassProps { user: InferModel; clerk: NonNullable>>; qrPayload: string; - guild:string; + guild: string; } export default async function Page() { @@ -38,96 +37,114 @@ export default async function Page() { if (!userDbRecord) return null; - const qrPayload = createQRpayload({ userID: user.id, createdAt: new Date() }); - const guild = Object.keys(c.groups)[userDbRecord.group]; + const qrPayload = createQRpayload({ + userID: user.id, + createdAt: new Date(), + }); + const guild = Object.keys(c.groups)[userDbRecord.group]; return ( -
+
- +
); } -function EventPass({ qrPayload, user, clerk,guild }: EventPassProps) { +function EventPass({ qrPayload, user, clerk, guild }: EventPassProps) { return ( -
-
-
-
- {`${user.firstName}'s -

- {user.firstName} -

-
-

@{user.hackerTag}

-

{guild}

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

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

-
-
-

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

-

{c.prettyLocation}

-
-
-
-
- - -
- -
-
- - - -
-
-

Psst! Click To Enlarge QR Code

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

+ {user.firstName} +

+
+

+ @{user.hackerTag} +

+

+ {guild} +

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

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

+
+
+

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

+

+ {c.prettyLocation} +

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

Psst! Click To Enlarge QR Code

+
+
+
+
+ ); } export const runtime = "edge"; diff --git a/apps/web/src/app/dash/schedule/page.tsx b/apps/web/src/app/dash/schedule/page.tsx index 867470b0..3650b296 100644 --- a/apps/web/src/app/dash/schedule/page.tsx +++ b/apps/web/src/app/dash/schedule/page.tsx @@ -4,21 +4,19 @@ import { db } from "db"; import { format, compareAsc } from "date-fns"; import { type ReactNode } from "react"; import { formatInTimeZone } from "date-fns-tz"; -import { redirect } from 'next/navigation'; +import { redirect } from "next/navigation"; export default async function Page() { - const events = await db.query.events.findMany(); - return ( -
-

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

-

- Christian

-
- ); +
+

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

+

- Christian

+
+ ); } export const runtime = "edge"; diff --git a/apps/web/src/app/dash/team/new/page.tsx b/apps/web/src/app/dash/team/new/page.tsx index 08237ec2..5d54fe40 100644 --- a/apps/web/src/app/dash/team/new/page.tsx +++ b/apps/web/src/app/dash/team/new/page.tsx @@ -3,14 +3,14 @@ import NewTeamForm from "@/components/dash/main/team/NewTeam"; export default async function Page() { return ( -
-
+
+

{c.hackathonName}

-

+

Team

-
-

New Team

+
+

New Team

diff --git a/apps/web/src/app/dash/team/page.tsx b/apps/web/src/app/dash/team/page.tsx index ffc13e25..302bca9e 100644 --- a/apps/web/src/app/dash/team/page.tsx +++ b/apps/web/src/app/dash/team/page.tsx @@ -40,17 +40,20 @@ export default async function Page() { if (!user.teamID) { return ( -
-
+
+

{c.hackathonName}

-

+

Team

-
-
+
+

You are not currently in a team.

- + How do Teams work?
@@ -63,16 +66,25 @@ export default async function Page() {
-
-

Invitations

+
+

+ Invitations +

{user.invites.length > 0 ? ( user.invites.map((invite) => ( -
-
-

{invite.team.name}

-

~{invite.team.tag}

+
+
+

+ {invite.team.name} +

+

+ ~{invite.team.tag} +

-
+
@@ -91,11 +103,11 @@ export default async function Page() { if (!user.team) return null; const team = user.team; return ( -
-
+
+
-

+

Team

@@ -107,9 +119,9 @@ export default async function Page() {
-
-
-
+
+
+
{`Team
-

{team.name}

-

~{team.tag}

-

{team.bio}

-
+

+ {team.name} +

+

+ ~{team.tag} +

+

{team.bio}

+
- Est. {team.createdAt.toDateString().split(" ").slice(1).join(" ")} + Est.{" "} + {team.createdAt + .toDateString() + .split(" ") + .slice(1) + .join(" ")}
( -
-
+
+
{`${member.hackerTag}'s

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

-

+

@{member.hackerTag}

diff --git a/apps/web/src/app/discord-verify/linked/page.tsx b/apps/web/src/app/discord-verify/linked/page.tsx index 1d41de77..5c50965a 100644 --- a/apps/web/src/app/discord-verify/linked/page.tsx +++ b/apps/web/src/app/discord-verify/linked/page.tsx @@ -5,17 +5,19 @@ import { CheckCircleIcon } from "lucide-react"; export default function Page() { return ( -
-
-

Discord Verification

-

+
+
+

+ Discord Verification +

+

Your Discord account is Linked!


-

- To unlink, go to your {c.hackathonName} account settings to unlink before linking a new - one. +

+ To unlink, go to your {c.hackathonName} account settings to + unlink before linking a new one.

diff --git a/apps/web/src/app/discord-verify/page.tsx b/apps/web/src/app/discord-verify/page.tsx index 676b9c34..ff8e88c7 100644 --- a/apps/web/src/app/discord-verify/page.tsx +++ b/apps/web/src/app/discord-verify/page.tsx @@ -54,7 +54,10 @@ export default async function Page({ const verification = await db.query.discordVerification.findFirst({ where: and( eq(discordVerification.code, passedCode), - or(eq(discordVerification.status, "pending"), eq(discordVerification.status, "expired")) + or( + eq(discordVerification.status, "pending"), + eq(discordVerification.status, "expired"), + ), ), }); @@ -71,10 +74,10 @@ export default async function Page({ .set({ status: "expired" }) .where(eq(discordVerification.code, passedCode)); return ( -
+

- This verification link has expired. Please click the verify button in discord again to - generate a new one. + This verification link has expired. Please click the verify + button in discord again to generate a new one.

); @@ -87,14 +90,14 @@ export default async function Page({ return ( <> -
-
+
+
Discord Profile Photo @@ -102,11 +105,11 @@ export default async function Page({ height={100} width={100} alt="Discord Profile Photo" - className="rounded-full aspect-square max-w-[75px]" + className="aspect-square max-w-[75px] rounded-full" src={c.icon.md} />
-

+

Link @{verification.discordName} to your
diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index 2d8a8675..e9da860e 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -42,8 +42,6 @@ --gradient-color-2: #94a33d; --gradient-color-3: #babc34; --gradient-color-4: #b5bf63; - - } .dark { --background: 240 10% 3.9%; @@ -79,7 +77,6 @@ } } - @layer base { * { @apply border-border; @@ -88,14 +85,13 @@ @apply bg-background text-foreground; } } -@layer utilities{ +@layer utilities { .arrow_animate { - animation: arrow 2s infinite; - } + animation: arrow 2s infinite; + } } @keyframes arrow { 0% { - transform: translate(0px); } @@ -106,7 +102,6 @@ 100% { transform: translate(0px); } - } .no-select { @@ -144,7 +139,6 @@ @keyframes arrow { 0% { - transform: translate(0px); } diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 7276068c..bb38418d 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -4,7 +4,11 @@ import { cookies } from "next/headers"; import { Analytics } from "@vercel/analytics/react"; import { defaultTheme } from "config"; -export default function RootLayout({ children }: { children: React.ReactNode }) { +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { const theme = cookies().get("hk_theme")?.value || defaultTheme; return ( diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 1ac1956d..d83a943a 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -18,7 +18,7 @@ const oswald = Oswald({ export default function Home() { return ( -
+
diff --git a/apps/web/src/app/register/page.tsx b/apps/web/src/app/register/page.tsx index e5ed350b..188ce88a 100644 --- a/apps/web/src/app/register/page.tsx +++ b/apps/web/src/app/register/page.tsx @@ -28,31 +28,40 @@ export default async function Page() { return redirect("/dash"); } - const [defaultRegistrationEnabled, defaultSecretRegistrationEnabled]: (string | null)[] = - await kv.mget( - "config:registration:registrationEnabled", - "config:registration:secretRegistrationEnabled" - ); + const [defaultRegistrationEnabled, defaultSecretRegistrationEnabled]: ( + | string + | null + )[] = await kv.mget( + "config:registration:registrationEnabled", + "config:registration:secretRegistrationEnabled", + ); if (parseRedisBoolean(defaultRegistrationEnabled, true) === true) { return ( <>
-
-

Register

+
+

+ Register +

- Welcome Hacker! Please fill out the form below to - complete your registration for {c.hackathonName}. + Welcome Hacker!{" "} + Please fill out the form below to complete your + registration for {c.hackathonName}.

-

+

Psttt... Running into a issue? Please let us know on{" "} Discord !

- +
@@ -60,26 +69,28 @@ export default async function Page() { } return ( -
-
+
+

{c.hackathonName}

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

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

Registration

-
-

Registration Is Currently Closed

+
+

+ Registration Is Currently Closed +

- If you believe this is a mistake or have any questions, feel free to reach out to us at{" "} - {c.issueEmail}! + If you believe this is a mistake or have any questions, feel + free to reach out to us at {c.issueEmail}!

-

+

Already registered? - + Sign-in.

diff --git a/apps/web/src/app/rsvp/page.tsx b/apps/web/src/app/rsvp/page.tsx index b5bf90ec..0bd5d2cd 100644 --- a/apps/web/src/app/rsvp/page.tsx +++ b/apps/web/src/app/rsvp/page.tsx @@ -41,16 +41,21 @@ export default async function RsvpPage({ // TODO: fix type jank here if ( - parseRedisBoolean(rsvpEnabled as string | boolean | null | undefined, true) === true || + parseRedisBoolean( + rsvpEnabled as string | boolean | null | undefined, + true, + ) === true || user.rsvp === true ) { return ( <> -
-
-

{c.hackathonName}

-

+
+
+

+ {c.hackathonName} +

+

RSVP

@@ -59,19 +64,20 @@ export default async function RsvpPage({ ); } else { return ( -
-
+
+

{c.hackathonName}

-

+

RSVP

-
-

+
+

RSVPs Are Currently Closed

-

- We have currently reached capacity for RSVPs. However, we still encourage you to show up - for walk-ins! If you have any questions or concerns, feel free to ask on{" "} +

+ We have currently reached capacity for RSVPs. However, + we still encourage you to show up for walk-ins! If you + have any questions or concerns, feel free to ask on{" "} Discord {" "} diff --git a/apps/web/src/app/schedule/[id]/page.tsx b/apps/web/src/app/schedule/[id]/page.tsx index d40e6633..dc45b9c6 100644 --- a/apps/web/src/app/schedule/[id]/page.tsx +++ b/apps/web/src/app/schedule/[id]/page.tsx @@ -8,7 +8,10 @@ import Navbar from "@/components/shared/Navbar"; export default async function Page({ params }: { params: { id: string } }) { if (!params || !params.id || isNaN(parseInt(params.id))) { return ( - + ); } @@ -20,7 +23,9 @@ export default async function Page({ params }: { params: { id: string } }) { return ( ); } diff --git a/apps/web/src/app/settings/account/page.tsx b/apps/web/src/app/settings/account/page.tsx index 3602f6e0..c9cd28cf 100644 --- a/apps/web/src/app/settings/account/page.tsx +++ b/apps/web/src/app/settings/account/page.tsx @@ -6,13 +6,13 @@ import { db } from "db"; 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!), - }); - if (!user) return redirect("/sign-in"); - return ; + const { userId } = auth(); + const user = await db.query.users.findFirst({ + with: { registrationData: true }, + where: eq(users.clerkID, userId!), + }); + if (!user) return redirect("/sign-in"); + return ; } export const runtime = "edge"; diff --git a/apps/web/src/app/settings/layout.tsx b/apps/web/src/app/settings/layout.tsx index 248f704c..fbfb40d2 100644 --- a/apps/web/src/app/settings/layout.tsx +++ b/apps/web/src/app/settings/layout.tsx @@ -7,39 +7,39 @@ import { Settings } from "lucide-react"; import ClientToast from "@/components/shared/ClientToast"; export default async function ({ children }: { children: ReactNode }) { - const { userId } = await auth(); - const user = await currentUser(); + const { userId } = await auth(); + const user = await currentUser(); - if (!user || !userId) { - return redirect("/sign-in"); - } + if (!user || !userId) { + return redirect("/sign-in"); + } - if (!user.publicMetadata.registrationComplete) { - return redirect("/register"); - } + if (!user.publicMetadata.registrationComplete) { + return redirect("/register"); + } - return ( - <> - - -

-
-
-
-

- - Settings -

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

+ + Settings +

+
+
+
+
+ {/* */} + + +
+
{children}
+
+ + ); } diff --git a/apps/web/src/app/settings/profile/page.tsx b/apps/web/src/app/settings/profile/page.tsx index 3e243c50..6140f8bb 100644 --- a/apps/web/src/app/settings/profile/page.tsx +++ b/apps/web/src/app/settings/profile/page.tsx @@ -5,22 +5,22 @@ import { eq } from "db/drizzle"; import { auth } from "@clerk/nextjs"; export default async function Page() { - const { userId } = auth(); - if (!userId) throw new Error("User not found"); - const user = await db.query.users.findFirst({ - where: eq(users.clerkID, userId), - with: { - profileData: true, - registrationData: true, - }, - }); - if (!user) throw new Error("User not found"); - return ( - - ); + const { userId } = auth(); + if (!userId) throw new Error("User not found"); + const user = await db.query.users.findFirst({ + where: eq(users.clerkID, userId), + with: { + profileData: true, + registrationData: true, + }, + }); + if (!user) throw new Error("User not found"); + return ( + + ); } export const runtime = "edge"; diff --git a/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx b/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx index 7be35932..057a6407 100644 --- a/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx +++ b/apps/web/src/app/sign-up/[[...sign-up]]/page.tsx @@ -6,40 +6,47 @@ import { Button } from "@/components/shadcn/ui/button"; import Link from "next/link"; export default async function Page() { - const [defaultRegistrationEnabled, defaultSecretRegistrationEnabled]: (string | null)[] = - await kv.mget( - "config:registration:registrationEnabled", - "config:registration:secretRegistrationEnabled" - ); + const [defaultRegistrationEnabled, defaultSecretRegistrationEnabled]: ( + | string + | null + )[] = await kv.mget( + "config:registration:registrationEnabled", + "config:registration:secretRegistrationEnabled", + ); if (parseRedisBoolean(defaultRegistrationEnabled, true) === true) { return (
- +
); } return ( -
-
+
+

{c.hackathonName}

-

+

Registration

-
-

Registration Is Currently Closed

+
+

+ Registration Is Currently Closed +

- If you believe this is a mistake or have any questions, feel free to reach out to us at{" "} - {c.issueEmail}! + If you believe this is a mistake or have any questions, feel + free to reach out to us at {c.issueEmail}!

-

+

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

diff --git a/apps/web/src/app/team/[tag]/page.tsx b/apps/web/src/app/team/[tag]/page.tsx index 49ffd857..e1a0c5ab 100644 --- a/apps/web/src/app/team/[tag]/page.tsx +++ b/apps/web/src/app/team/[tag]/page.tsx @@ -25,9 +25,9 @@ export default async function Page({ params }: { params: { tag: string } }) { return ( <> -
-
-
+
+
+
{`Team
-

{team.name}

- {team.bio && team.bio.length > 0 &&

{team.bio}

} -
+

{team.name}

+ {team.bio && team.bio.length > 0 && ( +

{team.bio}

+ )} +
{team.members.map((member) => ( -
+
{`${member.hackerTag}'s -
+

{member.firstName} {member.lastName}

-

+

@{member.hackerTag}

diff --git a/apps/web/src/app/user/[tag]/page.tsx b/apps/web/src/app/user/[tag]/page.tsx index dac2a9bd..db7a3dc7 100644 --- a/apps/web/src/app/user/[tag]/page.tsx +++ b/apps/web/src/app/user/[tag]/page.tsx @@ -22,11 +22,11 @@ export default async function ({ params }: { params: { tag: string } }) { return ( <> -
-
-
+
+
+
-
+
-

+

{user.firstName} {user.lastName}

-
-

@{user.hackerTag}

+
+

+ @{user.hackerTag} +

- {user.registrationData.GitHub && user.registrationData.GitHub.length > 0 && ( - - - {user.registrationData.GitHub} - - )} - {user.registrationData.LinkedIn && user.registrationData.LinkedIn.length > 0 && ( - - - {user.registrationData.LinkedIn} - - )} + {user.registrationData.GitHub && + user.registrationData.GitHub.length > 0 && ( + + + {user.registrationData.GitHub} + + )} + {user.registrationData.LinkedIn && + user.registrationData.LinkedIn.length > 0 && ( + + + {user.registrationData.LinkedIn} + + )} {user.registrationData.PersonalWebsite && - user.registrationData.PersonalWebsite.length > 0 && ( + user.registrationData.PersonalWebsite.length > + 0 && ( - {user.registrationData.PersonalWebsite.replace("https://", "").replace( - "http://", - "" - )} + {user.registrationData.PersonalWebsite.replace( + "https://", + "", + ).replace("http://", "")} )}
-
+

About

{user.profileData.bio}

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

Skills

-

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

+

Skills

+

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

) : null}
diff --git a/apps/web/src/components/admin/events/EventColumns.tsx b/apps/web/src/components/admin/events/EventColumns.tsx index 863fe428..792da88f 100644 --- a/apps/web/src/components/admin/events/EventColumns.tsx +++ b/apps/web/src/components/admin/events/EventColumns.tsx @@ -39,7 +39,9 @@ export const columns: ColumnDef[] = [ variant={"outline"} style={{ borderColor: - (c.eventTypes as Record)[row.original.type] || c.eventTypes.Other, + (c.eventTypes as Record)[ + row.original.type + ] || c.eventTypes.Other, }} > {row.original.type} diff --git a/apps/web/src/components/admin/events/EventDataTable.tsx b/apps/web/src/components/admin/events/EventDataTable.tsx index 5981fae5..0af4a9dc 100644 --- a/apps/web/src/components/admin/events/EventDataTable.tsx +++ b/apps/web/src/components/admin/events/EventDataTable.tsx @@ -1,6 +1,11 @@ "use client"; -import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"; +import { + ColumnDef, + flexRender, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table"; import { Table, @@ -19,7 +24,10 @@ interface DataTableProps { data: TData[]; } -export function DataTable({ columns, data }: DataTableProps) { +export function DataTable({ + columns, + data, +}: DataTableProps) { const table = useReactTable({ data, columns, @@ -37,7 +45,11 @@ export function DataTable({ columns, data }: DataTableProps {header.isPlaceholder ? null - : flexRender(header.column.columnDef.header, header.getContext())} + : flexRender( + header.column.columnDef + .header, + header.getContext(), + )} ); })} @@ -47,17 +59,26 @@ export function DataTable({ columns, data }: DataTableProps {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( - + {row.getVisibleCells().map((cell) => ( - {flexRender(cell.column.columnDef.cell, cell.getContext())} + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} ))} )) ) : ( - + No results. diff --git a/apps/web/src/components/admin/events/NewEventForm.tsx b/apps/web/src/components/admin/events/NewEventForm.tsx index 5c64ded5..e20a7bed 100644 --- a/apps/web/src/components/admin/events/NewEventForm.tsx +++ b/apps/web/src/components/admin/events/NewEventForm.tsx @@ -39,7 +39,7 @@ interface NewEventFormProps { const formSchema = newEventValidator.merge( z.object({ type: z.enum(Object.keys(c.eventTypes) as any), - }) + }), ); export default function NewEventForm({ defaultDate }: NewEventFormProps) { @@ -71,7 +71,10 @@ export default function NewEventForm({ defaultDate }: NewEventFormProps) { alert("Event Created Successfully! Redirecting to event page..."); router.push(res.data.redirect); } else { - alert("Failed to create event, please try again. Error:\n\n" + res.error); + alert( + "Failed to create event, please try again. Error:\n\n" + + res.error, + ); } } @@ -87,7 +90,10 @@ export default function NewEventForm({ defaultDate }: NewEventFormProps) { - Generally its best to keep this short and consise + + Generally its best to keep this short and + consise + )} @@ -115,7 +121,10 @@ export default function NewEventForm({ defaultDate }: NewEventFormProps) { render={({ field }) => ( Event Type - @@ -123,11 +132,16 @@ export default function NewEventForm({ defaultDate }: NewEventFormProps) { - {Object.keys(c.eventTypes).map((type) => ( - - {type} - - ))} + {Object.keys(c.eventTypes).map( + (type) => ( + + {type} + + ), + )} @@ -142,7 +156,10 @@ export default function NewEventForm({ defaultDate }: NewEventFormProps) { Host (Optional) - + @@ -159,11 +176,20 @@ export default function NewEventForm({ defaultDate }: NewEventFormProps) { { - field.onChange(!!date ? date.toDate(getLocalTimeZone()) : null); + field.onChange( + !!date + ? date.toDate( + getLocalTimeZone(), + ) + : null, + ); }} shouldCloseOnSelect={false} granularity={"minute"} @@ -182,11 +208,20 @@ export default function NewEventForm({ defaultDate }: NewEventFormProps) { { - field.onChange(!!date ? date.toDate(getLocalTimeZone()) : null); + field.onChange( + !!date + ? date.toDate( + getLocalTimeZone(), + ) + : null, + ); }} shouldCloseOnSelect={false} granularity={"minute"} diff --git a/apps/web/src/components/admin/landing/Overview.tsx b/apps/web/src/components/admin/landing/Overview.tsx index 3f21f808..a956d5e6 100644 --- a/apps/web/src/components/admin/landing/Overview.tsx +++ b/apps/web/src/components/admin/landing/Overview.tsx @@ -1,6 +1,14 @@ "use client"; -import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis, Tooltip, CartesianGrid } from "recharts"; +import { + Bar, + BarChart, + ResponsiveContainer, + XAxis, + YAxis, + Tooltip, + CartesianGrid, +} from "recharts"; import { parseISO, format } from "date-fns"; type DateNumberMap = { [key: string]: number }; @@ -22,7 +30,13 @@ export function Overview({ rawData }: OverviewProps) { return ( - + - + ); diff --git a/apps/web/src/components/admin/scanner/CheckinScanner.tsx b/apps/web/src/components/admin/scanner/CheckinScanner.tsx index b21542a4..57283642 100644 --- a/apps/web/src/components/admin/scanner/CheckinScanner.tsx +++ b/apps/web/src/components/admin/scanner/CheckinScanner.tsx @@ -46,11 +46,11 @@ export default function CheckinScanner({ hasScanned, checkedIn, scanUser, - hasRSVP + hasRSVP, }: CheckinScannerProps) { const [scanLoading, setScanLoading] = useState(false); const { execute: runScanAction } = useAction(checkInUser, {}); - const [proceed,setProceed] = useState(hasRSVP); + const [proceed, setProceed] = useState(hasRSVP); useEffect(() => { if (hasScanned) { setScanLoading(false); @@ -78,121 +78,149 @@ export default function CheckinScanner({ } return ( - <> -
-
-
- { - const params = new URLSearchParams(searchParams.toString()); - if (!params.has("user")) { - setScanLoading(true); - const qrParsedData = superjson.parse(result); - params.set("user", qrParsedData.userID); - params.set( - "createdAt", - qrParsedData.createdAt.getTime().toString() - ); - router.replace(`${path}?${params.toString()}`); - } - }} - onError={(error) => console.log(error?.message)} - containerStyle={{ - width: "100vw", - maxWidth: "500px", - margin: "0", - }} - /> -
- {/*
+ <> +
+
+
+ { + const params = new URLSearchParams( + searchParams.toString(), + ); + if (!params.has("user")) { + setScanLoading(true); + const qrParsedData = + superjson.parse( + result, + ); + params.set("user", qrParsedData.userID); + params.set( + "createdAt", + qrParsedData.createdAt + .getTime() + .toString(), + ); + router.replace( + `${path}?${params.toString()}`, + ); + } + }} + onError={(error) => console.log(error?.message)} + containerStyle={{ + width: "100vw", + maxWidth: "500px", + margin: "0", + }} + /> +
+ {/*
*/} -
-
- router.replace(path)} - open={hasScanned || scanLoading}> - - {scanLoading ? ( - <> - - Loading Scan... - {/* */} - - - - - - ) : ( - <> - - {checkedIn ? ( - - User already checked in! - - ) : ( - <> - {!proceed ? ( - <> - - Warning! - - - {scanUser?.firstName} {scanUser?.lastName} Is not - RSVP'd - - - Do you wish to proceed? - - - - - ) : ( - <> - New Scan - - New scan for {scanUser?.firstName}{" "} - {scanUser?.lastName} - - - )} - - )} - - { - proceed ? - <> - - {!checkedIn && ( - - )} - - - : - <> - } - - )} - - - - ); +
+
+ router.replace(path)} + open={hasScanned || scanLoading} + > + + {scanLoading ? ( + <> + + Loading Scan... + {/* */} + + + + + + ) : ( + <> + + {checkedIn ? ( + + User already checked in! + + ) : ( + <> + {!proceed ? ( + <> + + Warning! + + + {scanUser?.firstName}{" "} + {scanUser?.lastName} Is not + RSVP'd + + + Do you wish to proceed? + + + + + ) : ( + <> + + New Scan + + + New scan for{" "} + {scanUser?.firstName}{" "} + {scanUser?.lastName} + + + )} + + )} + + {proceed ? ( + <> + + {!checkedIn && ( + + )} + + + + ) : ( + <> + )} + + )} + + + + ); } diff --git a/apps/web/src/components/admin/scanner/PassScanner.tsx b/apps/web/src/components/admin/scanner/PassScanner.tsx index 98b2f67d..0edd89ca 100644 --- a/apps/web/src/components/admin/scanner/PassScanner.tsx +++ b/apps/web/src/components/admin/scanner/PassScanner.tsx @@ -9,16 +9,15 @@ 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, + 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"; @@ -36,145 +35,173 @@ scan: the scan object that has been scanned. If they have not scanned before sca */ interface PassScannerProps { - event: eventType; - hasScanned: boolean; - scan: scansType | null; - scanUser: userType | null; + event: eventType; + hasScanned: boolean; + scan: scansType | null; + scanUser: userType | null; } export default function PassScanner({ - event, - hasScanned, - scan, - scanUser, + event, + hasScanned, + scan, + scanUser, }: PassScannerProps) { - const [scanLoading, setScanLoading] = useState(false); - const { execute: runScanAction } = useAction(createScan, {}); - - useEffect(() => { - if (hasScanned) { - setScanLoading(false); - } - }, [hasScanned]); - - const searchParams = useSearchParams(); - const path = usePathname(); - const router = useRouter(); - - const register = (scanUser?.checkedIn) ? "Checked in!" : "Not Checked In"; - const guild = Object.keys(c.groups)[scanUser?.group || 0] ?? "None"; - const role = (scanUser?.role) ? scanUser?.role : "Not Found"; - - - - function handleScanCreate() { - const params = new URLSearchParams(searchParams.toString()); - const timestamp = parseInt(params.get("createdAt") as string); - if (isNaN(timestamp)) { - return alert("Invalid QR Code Data (Field: createdAt)"); - } - if (scan) { - runScanAction({ - eventID: event.id, - userID: scan.userID, - countToSet: scan.count + 1, - alreadyExists: true, - creationTime: new Date(timestamp), - }); - } else { - // TODO: make this a little more typesafe - runScanAction({ - eventID: event.id, - userID: scanUser?.clerkID as string, - countToSet: 1, - alreadyExists: false, - creationTime: new Date(timestamp), - }); - } - - toast.success("Successfully Scanned User In"); - router.replace(`${path}`); - } - - return ( - <> -
-
-
- { - const params = new URLSearchParams(searchParams.toString()); - if (!params.has("user")) { - setScanLoading(true); - const qrParsedData = superjson.parse(result); - params.set("user", qrParsedData.userID); - params.set( - "createdAt", - qrParsedData.createdAt.getTime().toString() - ); - router.replace(`${path}?${params.toString()}`); - } - }} - onError={(error) => console.log(error?.message)} - containerStyle={{ - width: "100vw", - maxWidth: "500px", - margin: "0", - }} - /> -
-
- - - -
-
-
- router.replace(path)} - open={hasScanned || scanLoading}> - - {scanLoading ? ( - <> - - Loading Scan... - - - - - - - ) : ( - <> - - New Scan for {event.title} - - <> - {scanUser?.firstName} {scanUser?.lastName} - -

- Role: {role} -

-

- Status: {register} -

-

Guild: {guild}

-
-
- - - - - - )} -
-
- - ); + const [scanLoading, setScanLoading] = useState(false); + const { execute: runScanAction } = useAction(createScan, {}); + + useEffect(() => { + if (hasScanned) { + setScanLoading(false); + } + }, [hasScanned]); + + const searchParams = useSearchParams(); + const path = usePathname(); + const router = useRouter(); + + const register = scanUser?.checkedIn ? "Checked in!" : "Not Checked In"; + const guild = Object.keys(c.groups)[scanUser?.group || 0] ?? "None"; + const role = scanUser?.role ? scanUser?.role : "Not Found"; + + function handleScanCreate() { + const params = new URLSearchParams(searchParams.toString()); + const timestamp = parseInt(params.get("createdAt") as string); + if (isNaN(timestamp)) { + return alert("Invalid QR Code Data (Field: createdAt)"); + } + if (scan) { + runScanAction({ + eventID: event.id, + userID: scan.userID, + countToSet: scan.count + 1, + alreadyExists: true, + creationTime: new Date(timestamp), + }); + } else { + // TODO: make this a little more typesafe + runScanAction({ + eventID: event.id, + userID: scanUser?.clerkID as string, + countToSet: 1, + alreadyExists: false, + creationTime: new Date(timestamp), + }); + } + + toast.success("Successfully Scanned User In"); + router.replace(`${path}`); + } + + return ( + <> +
+
+
+ { + const params = new URLSearchParams( + searchParams.toString(), + ); + if (!params.has("user")) { + setScanLoading(true); + const qrParsedData = + superjson.parse( + result, + ); + params.set("user", qrParsedData.userID); + params.set( + "createdAt", + qrParsedData.createdAt + .getTime() + .toString(), + ); + router.replace( + `${path}?${params.toString()}`, + ); + } + }} + onError={(error) => console.log(error?.message)} + containerStyle={{ + width: "100vw", + maxWidth: "500px", + margin: "0", + }} + /> +
+
+ + + +
+
+
+ router.replace(path)} + open={hasScanned || scanLoading} + > + + {scanLoading ? ( + <> + + Loading Scan... + + + + + + + ) : ( + <> + + + New Scan for {event.title} + + + <> + {scanUser?.firstName}{" "} + {scanUser?.lastName} + +

+ Role:{" "} + {role} +

+

+ + Status: + {" "} + {register} +

+

+ + Guild: + {" "} + {guild} +

+
+
+ + + + + + )} +
+
+ + ); } diff --git a/apps/web/src/components/admin/toggles/NavItemsManager.tsx b/apps/web/src/components/admin/toggles/NavItemsManager.tsx index 5239e139..ca6c230c 100644 --- a/apps/web/src/components/admin/toggles/NavItemsManager.tsx +++ b/apps/web/src/components/admin/toggles/NavItemsManager.tsx @@ -25,7 +25,11 @@ import { Label } from "@/components/shadcn/ui/label"; import { Plus } from "lucide-react"; import { useState } from "react"; import { useAction, useOptimisticAction } from "next-safe-action/hook"; -import { setItem, removeItem, toggleItem } from "@/actions/admin/modify-nav-item"; +import { + setItem, + removeItem, + toggleItem, +} from "@/actions/admin/modify-nav-item"; import { toast } from "sonner"; import Link from "next/link"; import { Switch } from "@/components/shadcn/ui/switch"; @@ -57,7 +61,9 @@ export function NavItemsManager({ navItems }: NavItemsManagerProps) { {navItems.map((item) => ( - {item.name} + + {item.name} + {item.url} @@ -68,10 +74,15 @@ export function NavItemsManager({ navItems }: NavItemsManagerProps) { checked={item.enabled} onCheckedChange={(checked) => didToggle(item.name, checked)} /> */} - + - - + + -
- ); -} \ No newline at end of file + return ( +
+ {/* Needs to clear text */} + handleSearch(e.target.value)} + /> + +
+ ); +} diff --git a/apps/web/src/components/admin/users/ServerSections.tsx b/apps/web/src/components/admin/users/ServerSections.tsx index 70d49ad9..0bb58bb0 100644 --- a/apps/web/src/components/admin/users/ServerSections.tsx +++ b/apps/web/src/components/admin/users/ServerSections.tsx @@ -11,10 +11,18 @@ export function PersonalInfo({ user }: { user: UserWithAllData }) {
- + - +
@@ -27,13 +35,28 @@ export function ProfileInfo({ user }: { user: UserWithAllData }) {
- - - - - + + + + +
-
+
@@ -76,7 +99,14 @@ export function TeamInfo({ user }: { user: UserWithAllData }) { <> - + ) : null}
@@ -89,10 +119,16 @@ export function TeamInfo({ user }: { user: UserWithAllData }) { ); } -function Cell({ title, value }: { title: string; value: string | number | boolean }) { +function Cell({ + title, + value, +}: { + title: string; + value: string | number | boolean; +}) { return (
-

{title}

+

{title}

{value.toString()}

); diff --git a/apps/web/src/components/admin/users/UpdateRoleDialog.tsx b/apps/web/src/components/admin/users/UpdateRoleDialog.tsx index 3b5d637c..edb2d349 100644 --- a/apps/web/src/components/admin/users/UpdateRoleDialog.tsx +++ b/apps/web/src/components/admin/users/UpdateRoleDialog.tsx @@ -60,7 +60,9 @@ export default function UpdateRoleDialog({ Update {name}'s Role - Update the role of any user on HackKit. + + Update the role of any user on HackKit. +
@@ -73,16 +75,29 @@ export default function UpdateRoleDialog({ placeholder="@HackerTag" className="col-span-3" /> */} - + setRoleToSet(v as (typeof perms)[number]) + } + > - + {/* Light Dark System */} {perms.map((perm) => { - if (!canMakeAdmins && (perm === "admin" || perm === "super_admin")) return null; + if ( + !canMakeAdmins && + (perm === "admin" || + perm === "super_admin") + ) + return null; return ( {titleCase(perm.replace("_", " "))} @@ -95,16 +110,22 @@ export default function UpdateRoleDialog({
{roleToSet !== currPermision ? ( -
- {titleCase(currPermision.replace("_", " "))} +
+ + {titleCase(currPermision.replace("_", " "))} + - {titleCase(roleToSet.replace("_", " "))} + + {titleCase(roleToSet.replace("_", " "))} +
) : null}