From f858b949c4eda7cfbaafabe7ce0ead3c38e09c62 Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Fri, 27 Jun 2025 17:38:12 -0500
Subject: [PATCH 01/13] updates to use bucket-env
---
apps/infrastructure-migrator/driver.ts | 2 +-
apps/web/src/app/api/upload/pfp/route.ts | 5 ++++-
apps/web/src/app/api/upload/resume/register/route.ts | 5 ++++-
apps/web/src/app/api/upload/resume/view/route.ts | 2 +-
apps/web/src/app/register/page.tsx | 4 ++--
apps/web/src/lib/utils/server/file-upload.ts | 2 +-
packages/config/hackkit.config.ts | 1 -
7 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/apps/infrastructure-migrator/driver.ts b/apps/infrastructure-migrator/driver.ts
index ffbab1e7..012bdc1e 100644
--- a/apps/infrastructure-migrator/driver.ts
+++ b/apps/infrastructure-migrator/driver.ts
@@ -240,7 +240,7 @@ async function migratePostgresSqLite() {
const cmd = new PutObjectCommand({
Key: key,
- Bucket: staticUploads.bucketName,
+ Bucket: process.env.R2_BUCKET_NAME,
ContentType: "application/pdf",
///@ts-expect-error
Body: buffer,
diff --git a/apps/web/src/app/api/upload/pfp/route.ts b/apps/web/src/app/api/upload/pfp/route.ts
index 194633a3..187c34ae 100644
--- a/apps/web/src/app/api/upload/pfp/route.ts
+++ b/apps/web/src/app/api/upload/pfp/route.ts
@@ -26,7 +26,10 @@ export async function POST(request: Request): Promise {
const randomSeq = crypto.randomUUID();
const [fileName, extension] = body.fileName.split(".");
const key = `${body.location}/${fileName}-${randomSeq}.${extension}`;
- const url = await getPresignedUploadUrl(staticUploads.bucketName, key);
+ const url = await getPresignedUploadUrl(
+ process.env.R2_BUCKET_NAME!,
+ key,
+ );
return NextResponse.json({ url, key });
} catch (error) {
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 97957372..b05e53e6 100644
--- a/apps/web/src/app/api/upload/resume/register/route.ts
+++ b/apps/web/src/app/api/upload/resume/register/route.ts
@@ -25,7 +25,10 @@ export async function POST(request: Request): Promise {
const randomSeq = crypto.randomUUID();
const [fileName, extension] = body.fileName.split(".");
const key = `${body.location}/${fileName}-${randomSeq}.${extension}`;
- const url = await getPresignedUploadUrl(staticUploads.bucketName, key);
+ const url = await getPresignedUploadUrl(
+ process.env.R2_BUCKET_NAME!,
+ key,
+ );
return NextResponse.json({ url, key });
} catch (error) {
diff --git a/apps/web/src/app/api/upload/resume/view/route.ts b/apps/web/src/app/api/upload/resume/view/route.ts
index 26597166..5e54bf21 100644
--- a/apps/web/src/app/api/upload/resume/view/route.ts
+++ b/apps/web/src/app/api/upload/resume/view/route.ts
@@ -25,7 +25,7 @@ export async function GET(request: Request) {
// Presign the url and return redirect to it.
const presignedViewingUrl = await getPresignedViewingUrl(
- staticUploads.bucketName,
+ process.env.R2_BUCKET_NAME!,
decodedKey,
);
diff --git a/apps/web/src/app/register/page.tsx b/apps/web/src/app/register/page.tsx
index 686b872b..d26fb9ae 100644
--- a/apps/web/src/app/register/page.tsx
+++ b/apps/web/src/app/register/page.tsx
@@ -27,7 +27,7 @@ export default async function Page() {
"config:registration:secretRegistrationEnabled",
);
- if (parseRedisBoolean(defaultRegistrationEnabled, true) === true) {
+ // if (parseRedisBoolean(defaultRegistrationEnabled, true) === true) {
return (
<>
@@ -57,7 +57,7 @@ export default async function Page() {
>
);
- }
+ // }
return (
diff --git a/apps/web/src/lib/utils/server/file-upload.ts b/apps/web/src/lib/utils/server/file-upload.ts
index aa18e5d6..ac1c39be 100644
--- a/apps/web/src/lib/utils/server/file-upload.ts
+++ b/apps/web/src/lib/utils/server/file-upload.ts
@@ -7,7 +7,7 @@ export async function del(url: string): Promise {
const key = url.split("=")[1];
const cmd = new DeleteObjectCommand({
- Bucket: staticUploads.bucketName,
+ Bucket: process.env.R2_BUCKET_NAME,
Key: key,
});
diff --git a/packages/config/hackkit.config.ts b/packages/config/hackkit.config.ts
index e3a37bd8..47182221 100644
--- a/packages/config/hackkit.config.ts
+++ b/packages/config/hackkit.config.ts
@@ -986,7 +986,6 @@ const c = {
} as const;
const staticUploads = {
- bucketName: "acm-userdata",
bucketHost: "/api/upload/resume/view",
bucketResumeBaseUploadUrl: `${c.hackathonName}/${c.itteration}/resumes`,
} as const;
From 7cd32edba4ecfe4d6649aeb79031b3bb9a3495ca Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Fri, 27 Jun 2025 18:03:25 -0500
Subject: [PATCH 02/13] fix volunteer permissions
---
apps/web/src/actions/admin/user-actions.ts | 2 +-
apps/web/src/app/admin/layout.tsx | 7 ++++++-
apps/web/src/app/admin/page.tsx | 2 +-
apps/web/src/app/register/page.tsx | 4 ++--
apps/web/src/lib/safe-action.ts | 2 +-
apps/web/src/lib/utils/server/admin.ts | 2 +-
packages/config/hackkit.config.ts | 4 ++--
7 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/apps/web/src/actions/admin/user-actions.ts b/apps/web/src/actions/admin/user-actions.ts
index 41b46656..89be68af 100644
--- a/apps/web/src/actions/admin/user-actions.ts
+++ b/apps/web/src/actions/admin/user-actions.ts
@@ -23,7 +23,7 @@ export const updateRole = adminAction
}) => {
if (
user.role !== "super_admin" &&
- (roleToSet === "super_admin" || roleToSet === "admin")
+ (roleToSet === "super_admin" || roleToSet === "admin" || roleToSet === "volunteer")
) {
returnValidationErrors(z.null(), {
_errors: ["You are not allowed to do this!"],
diff --git a/apps/web/src/app/admin/layout.tsx b/apps/web/src/app/admin/layout.tsx
index 78698ce1..33375058 100644
--- a/apps/web/src/app/admin/layout.tsx
+++ b/apps/web/src/app/admin/layout.tsx
@@ -24,7 +24,12 @@ export default async function AdminLayout({ children }: AdminLayoutProps) {
const user = await getUser(userId);
- if (!user || (user.role !== "admin" && user.role !== "super_admin")) {
+ if (
+ !user ||
+ (user.role !== "admin" &&
+ user.role !== "super_admin" &&
+ user.role !== "volunteer")
+ ) {
console.log("Denying admin access to user", user);
return (
@@ -57,7 +57,7 @@ export default async function Page() {
>
);
- // }
+ }
return (
diff --git a/apps/web/src/lib/safe-action.ts b/apps/web/src/lib/safe-action.ts
index 8755206f..8beedfec 100644
--- a/apps/web/src/lib/safe-action.ts
+++ b/apps/web/src/lib/safe-action.ts
@@ -23,7 +23,7 @@ export const authenticatedAction = publicAction.use(
export const adminAction = authenticatedAction.use(async ({ next, ctx }) => {
const user = await getUser(ctx.userId);
- if (!user || (user.role !== "admin" && user.role !== "super_admin")) {
+ if (!user || (user.role !== "admin" && user.role !== "super_admin" && user.role !== "volunteer")) {
returnValidationErrors(z.null(), {
_errors: ["Unauthorized (Not Admin)"],
});
diff --git a/apps/web/src/lib/utils/server/admin.ts b/apps/web/src/lib/utils/server/admin.ts
index 440a6c38..bd8032cf 100644
--- a/apps/web/src/lib/utils/server/admin.ts
+++ b/apps/web/src/lib/utils/server/admin.ts
@@ -1,5 +1,5 @@
import type { User } from "db/types";
export function isUserAdmin(user: User) {
- return user.role === "admin" || user.role === "super_admin";
+ return user.role === "admin" || user.role === "super_admin" || user.role === "volunteer";
}
diff --git a/packages/config/hackkit.config.ts b/packages/config/hackkit.config.ts
index 47182221..6dd26376 100644
--- a/packages/config/hackkit.config.ts
+++ b/packages/config/hackkit.config.ts
@@ -1020,8 +1020,8 @@ const publicRoutes = [
/^\/user\//,
"/404",
"/bugreport",
- "/sign-in",
- "/sign-up",
+ /^\/sign-in(\/.*)?$/,
+ /^\/sign-up(\/.*)?$/,
];
export default c;
From a101fd850095c36497142dfab5cdfb1d0495cee7 Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Fri, 27 Jun 2025 18:11:30 -0500
Subject: [PATCH 03/13] fix bug with local storage write
---
apps/web/src/components/registration/RegisterForm.tsx | 9 +++++++++
apps/web/src/lib/constants/index.ts | 2 +-
apps/web/src/validators/shared/registration.ts | 3 ---
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/apps/web/src/components/registration/RegisterForm.tsx b/apps/web/src/components/registration/RegisterForm.tsx
index 8c37967f..2a177a42 100644
--- a/apps/web/src/components/registration/RegisterForm.tsx
+++ b/apps/web/src/components/registration/RegisterForm.tsx
@@ -134,6 +134,7 @@ export default function RegisterForm({
const hackerFormData = localStorage.getItem(
HACKER_REGISTRATION_STORAGE_KEY,
);
+ console.log(hackerFormData)
if (hackerFormData) {
try {
const parsed = JSON.parse(hackerFormData);
@@ -185,6 +186,14 @@ export default function RegisterForm({
);
}
}
+ // else{
+ // localStorage.setItem(
+ // HACKER_REGISTRATION_STORAGE_KEY,
+ // JSON.stringify({
+ // ...form.getValues(),
+ // }),
+ // );
+ // }
}, []);
// seperate useffect for getting the resume file
diff --git a/apps/web/src/lib/constants/index.ts b/apps/web/src/lib/constants/index.ts
index 4a076d57..2de61d33 100644
--- a/apps/web/src/lib/constants/index.ts
+++ b/apps/web/src/lib/constants/index.ts
@@ -7,7 +7,7 @@ export const UNIQUE_KEY_CONSTRAINT_VIOLATION_CODE = "23505";
export const UNIQUE_KEY_MAPPER_DEFAULT_KEY =
"default" as keyof typeof c.db.uniqueKeyMapper;
export const PAYLOAD_TOO_LARGE_CODE = 413;
-export const HACKER_REGISTRATION_STORAGE_KEY = "hackerRegistrationData";
+export const HACKER_REGISTRATION_STORAGE_KEY = `${c.hackathonName}_${c.itteration}_hackerRegistrationData`;
export const HACKER_REGISTRATION_RESUME_STORAGE_KEY =
"hackerRegistrationResume";
export const NOT_LOCAL_SCHOOL = "NOT_LOCAL_SCHOOL";
diff --git a/apps/web/src/validators/shared/registration.ts b/apps/web/src/validators/shared/registration.ts
index 83f52c9f..fde03f7f 100644
--- a/apps/web/src/validators/shared/registration.ts
+++ b/apps/web/src/validators/shared/registration.ts
@@ -204,9 +204,6 @@ export const hackerRegistrationValidatorLocalStorage =
text: z.string().min(1).max(50),
}),
)
- .min(1, {
- message: "You must have at least one skill",
- })
.max(c.registration.maxNumberOfSkills, {
message: `You cannot have more than ${c.registration.maxNumberOfSkills} skills`,
}),
From 7865ac833ef36a2685d5e40b4fa98f7423c1e884 Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Fri, 27 Jun 2025 18:28:20 -0500
Subject: [PATCH 04/13] debounce registration write
---
.../components/registration/RegisterForm.tsx | 31 ++++++++++---------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/apps/web/src/components/registration/RegisterForm.tsx b/apps/web/src/components/registration/RegisterForm.tsx
index 2a177a42..b7df5fc2 100644
--- a/apps/web/src/components/registration/RegisterForm.tsx
+++ b/apps/web/src/components/registration/RegisterForm.tsx
@@ -81,6 +81,7 @@ import {
encodeFileAsBase64,
decodeBase64AsFile,
} from "@/lib/utils/shared/files";
+import { useDebouncedCallback } from "use-debounce";
export default function RegisterForm({
defaultEmail,
@@ -186,14 +187,6 @@ export default function RegisterForm({
);
}
}
- // else{
- // localStorage.setItem(
- // HACKER_REGISTRATION_STORAGE_KEY,
- // JSON.stringify({
- // ...form.getValues(),
- // }),
- // );
- // }
}, []);
// seperate useffect for getting the resume file
@@ -224,16 +217,24 @@ export default function RegisterForm({
}
}, []);
- // might be good to debounce later on
+ const debouncedLocalStorageWrite = useDebouncedCallback(
+ // function
+ () => {
+ localStorage.setItem(
+ HACKER_REGISTRATION_STORAGE_KEY,
+ JSON.stringify({
+ ...form.getValues(),
+ }),
+ );
+ },
+ 1000,
+ );
+
form.watch(() => {
- localStorage.setItem(
- HACKER_REGISTRATION_STORAGE_KEY,
- JSON.stringify({
- ...form.getValues(),
- }),
- );
+ debouncedLocalStorageWrite();
});
+
// use action logic
const { execute: runRegisterUser, reset: resetRegisterUser } = useAction(
registerHacker,
From 3828e4043fc867ce764084a0dcacb544b367925f Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Fri, 27 Jun 2025 18:35:16 -0500
Subject: [PATCH 05/13] remove silly alert
---
apps/web/src/components/registration/RegisterForm.tsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/apps/web/src/components/registration/RegisterForm.tsx b/apps/web/src/components/registration/RegisterForm.tsx
index b7df5fc2..272a8236 100644
--- a/apps/web/src/components/registration/RegisterForm.tsx
+++ b/apps/web/src/components/registration/RegisterForm.tsx
@@ -317,8 +317,6 @@ export default function RegisterForm({
},
);
- alert(uploadedFileUrl);
-
resume = uploadedFileUrl;
}
runRegisterUser({ ...data, resume });
From 891aac42b833aedd55258068b324ca911024c5cf Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Fri, 27 Jun 2025 18:35:30 -0500
Subject: [PATCH 06/13] formatter
---
apps/web/src/actions/admin/user-actions.ts | 4 +++-
apps/web/src/app/admin/page.tsx | 4 +++-
apps/web/src/components/registration/RegisterForm.tsx | 3 +--
apps/web/src/lib/safe-action.ts | 7 ++++++-
apps/web/src/lib/utils/server/admin.ts | 6 +++++-
5 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/apps/web/src/actions/admin/user-actions.ts b/apps/web/src/actions/admin/user-actions.ts
index 89be68af..919a9320 100644
--- a/apps/web/src/actions/admin/user-actions.ts
+++ b/apps/web/src/actions/admin/user-actions.ts
@@ -23,7 +23,9 @@ export const updateRole = adminAction
}) => {
if (
user.role !== "super_admin" &&
- (roleToSet === "super_admin" || roleToSet === "admin" || roleToSet === "volunteer")
+ (roleToSet === "super_admin" ||
+ roleToSet === "admin" ||
+ roleToSet === "volunteer")
) {
returnValidationErrors(z.null(), {
_errors: ["You are not allowed to do this!"],
diff --git a/apps/web/src/app/admin/page.tsx b/apps/web/src/app/admin/page.tsx
index 06acd700..1914bc73 100644
--- a/apps/web/src/app/admin/page.tsx
+++ b/apps/web/src/app/admin/page.tsx
@@ -19,7 +19,9 @@ export default async function Page() {
const adminUser = await getUser(userId);
if (
!adminUser ||
- (adminUser.role !== "admin" && adminUser.role !== "super_admin" && adminUser.role !== "volunteer")
+ (adminUser.role !== "admin" &&
+ adminUser.role !== "super_admin" &&
+ adminUser.role !== "volunteer")
) {
return notFound();
}
diff --git a/apps/web/src/components/registration/RegisterForm.tsx b/apps/web/src/components/registration/RegisterForm.tsx
index 272a8236..85abd141 100644
--- a/apps/web/src/components/registration/RegisterForm.tsx
+++ b/apps/web/src/components/registration/RegisterForm.tsx
@@ -135,7 +135,7 @@ export default function RegisterForm({
const hackerFormData = localStorage.getItem(
HACKER_REGISTRATION_STORAGE_KEY,
);
- console.log(hackerFormData)
+ console.log(hackerFormData);
if (hackerFormData) {
try {
const parsed = JSON.parse(hackerFormData);
@@ -234,7 +234,6 @@ export default function RegisterForm({
debouncedLocalStorageWrite();
});
-
// use action logic
const { execute: runRegisterUser, reset: resetRegisterUser } = useAction(
registerHacker,
diff --git a/apps/web/src/lib/safe-action.ts b/apps/web/src/lib/safe-action.ts
index 8beedfec..7ee73e3b 100644
--- a/apps/web/src/lib/safe-action.ts
+++ b/apps/web/src/lib/safe-action.ts
@@ -23,7 +23,12 @@ export const authenticatedAction = publicAction.use(
export const adminAction = authenticatedAction.use(async ({ next, ctx }) => {
const user = await getUser(ctx.userId);
- if (!user || (user.role !== "admin" && user.role !== "super_admin" && user.role !== "volunteer")) {
+ if (
+ !user ||
+ (user.role !== "admin" &&
+ user.role !== "super_admin" &&
+ user.role !== "volunteer")
+ ) {
returnValidationErrors(z.null(), {
_errors: ["Unauthorized (Not Admin)"],
});
diff --git a/apps/web/src/lib/utils/server/admin.ts b/apps/web/src/lib/utils/server/admin.ts
index bd8032cf..b62bf2a1 100644
--- a/apps/web/src/lib/utils/server/admin.ts
+++ b/apps/web/src/lib/utils/server/admin.ts
@@ -1,5 +1,9 @@
import type { User } from "db/types";
export function isUserAdmin(user: User) {
- return user.role === "admin" || user.role === "super_admin" || user.role === "volunteer";
+ return (
+ user.role === "admin" ||
+ user.role === "super_admin" ||
+ user.role === "volunteer"
+ );
}
From 4b4e46d99f5af2742286185de697578789a81d71 Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Wed, 2 Jul 2025 18:36:45 -0600
Subject: [PATCH 07/13] adds voluteer action
---
.../src/actions/admin/scanner-admin-actions.ts | 8 ++++----
apps/web/src/lib/safe-action.ts | 17 +++++++++++++++--
2 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/apps/web/src/actions/admin/scanner-admin-actions.ts b/apps/web/src/actions/admin/scanner-admin-actions.ts
index 60f1aeed..3447db0d 100644
--- a/apps/web/src/actions/admin/scanner-admin-actions.ts
+++ b/apps/web/src/actions/admin/scanner-admin-actions.ts
@@ -1,12 +1,12 @@
"use server";
-import { adminAction } from "@/lib/safe-action";
+import { volunteerAction } from "@/lib/safe-action";
import { z } from "zod";
import { db, sql } from "db";
import { scans, userCommonData } from "db/schema";
import { eq, and } from "db/drizzle";
-export const createScan = adminAction
+export const createScan = volunteerAction
.schema(
z.object({
eventID: z.number(),
@@ -49,7 +49,7 @@ export const createScan = adminAction
},
);
-export const getScan = adminAction
+export const getScan = volunteerAction
.schema(z.object({ eventID: z.number(), userID: z.string() }))
.action(
async ({
@@ -77,7 +77,7 @@ const checkInUserSchema = z.object({
}, "QR Code has expired. Please tell user refresh the QR Code"),
});
-export const checkInUserToHackathon = adminAction
+export const checkInUserToHackathon = volunteerAction
.schema(checkInUserSchema)
.action(async ({ parsedInput: { userID } }) => {
// Set checkinTimestamp
diff --git a/apps/web/src/lib/safe-action.ts b/apps/web/src/lib/safe-action.ts
index 7ee73e3b..587fd51d 100644
--- a/apps/web/src/lib/safe-action.ts
+++ b/apps/web/src/lib/safe-action.ts
@@ -5,6 +5,7 @@ import {
import { auth } from "@clerk/nextjs/server";
import { getUser } from "db/functions";
import { z } from "zod";
+import { isUserAdmin } from "./utils/server/admin";
export const publicAction = createSafeActionClient();
@@ -21,13 +22,25 @@ export const authenticatedAction = publicAction.use(
},
);
+export const volunteerAction = authenticatedAction.use(async ({ next, ctx }) => {
+ const user = await getUser(ctx.userId);
+ if (
+ !user ||
+ !["admin", "super_admin", "volunteer"].includes(user.role)
+ ) {
+ returnValidationErrors(z.null(), {
+ _errors: ["Unauthorized (Not Admin)"],
+ });
+ }
+ return next({ ctx: { user, ...ctx } });
+});
+
export const adminAction = authenticatedAction.use(async ({ next, ctx }) => {
const user = await getUser(ctx.userId);
if (
!user ||
(user.role !== "admin" &&
- user.role !== "super_admin" &&
- user.role !== "volunteer")
+ user.role !== "super_admin")
) {
returnValidationErrors(z.null(), {
_errors: ["Unauthorized (Not Admin)"],
From dbb2bc7427dd5c75819eb0916f06ae537518cfbf Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Wed, 2 Jul 2025 18:36:50 -0600
Subject: [PATCH 08/13] removes points
---
packages/config/hackkit.config.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/packages/config/hackkit.config.ts b/packages/config/hackkit.config.ts
index 6dd26376..ff57e404 100644
--- a/packages/config/hackkit.config.ts
+++ b/packages/config/hackkit.config.ts
@@ -912,11 +912,10 @@ const c = {
Overview: "/admin",
Users: "/admin/users",
Events: "/admin/events",
- Points: "/admin/points",
+ // Points: "/admin/points", -- commented out until implemented
"Hackathon Check-in": "/admin/check-in",
Toggles: "/admin/toggles",
},
- // TODO: Can remove days? Pretty sure they're dynamic now.
},
eventTypes: {
Meal: "#FFC107",
From 2023f55f502a99717fbaa59850f468b567fb4584 Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Wed, 2 Jul 2025 18:37:07 -0600
Subject: [PATCH 09/13] updates ui for volunteers
---
apps/web/src/app/admin/layout.tsx | 9 ++++++---
apps/web/src/app/admin/users/page.tsx | 11 +++++++++++
apps/web/src/lib/utils/server/admin.ts | 4 +---
3 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/apps/web/src/app/admin/layout.tsx b/apps/web/src/app/admin/layout.tsx
index 33375058..78f1889b 100644
--- a/apps/web/src/app/admin/layout.tsx
+++ b/apps/web/src/app/admin/layout.tsx
@@ -85,9 +85,12 @@ export default async function AdminLayout({ children }: AdminLayoutProps) {
- {Object.entries(c.dashPaths.admin).map(([name, path]) => (
-
- ))}
+ {Object.entries(c.dashPaths.admin).map(([name, path]) =>
+ ["Users","Toggles"].includes(name) &&
+ user.role === "volunteer" ? null : (
+
+ ),
+ )}
Loading...
}>{children}
>
diff --git a/apps/web/src/app/admin/users/page.tsx b/apps/web/src/app/admin/users/page.tsx
index a800519d..9cde884a 100644
--- a/apps/web/src/app/admin/users/page.tsx
+++ b/apps/web/src/app/admin/users/page.tsx
@@ -5,9 +5,20 @@ import { Button } from "@/components/shadcn/ui/button";
import { FolderInput } from "lucide-react";
import { getAllUsers } from "db/functions";
import { userCommonData } from "db/schema";
+import { getUser } from "db/functions";
+import { auth } from "@clerk/nextjs/server";
+import { notFound } from "next/navigation";
+import { isUserAdmin } from "@/lib/utils/server/admin";
// This begs a question where we might want to have an option later on to sort by the role as we might want different things
export default async function Page() {
+ const { userId } = await auth();
+
+ if (!userId) return notFound();
+
+ const admin = await getUser(userId);
+ if (!admin || !isUserAdmin(admin)) return notFound();
+
const userData = await getAllUsers();
return (
diff --git a/apps/web/src/lib/utils/server/admin.ts b/apps/web/src/lib/utils/server/admin.ts
index b62bf2a1..cd9b83db 100644
--- a/apps/web/src/lib/utils/server/admin.ts
+++ b/apps/web/src/lib/utils/server/admin.ts
@@ -2,8 +2,6 @@ import type { User } from "db/types";
export function isUserAdmin(user: User) {
return (
- user.role === "admin" ||
- user.role === "super_admin" ||
- user.role === "volunteer"
+ ["admin", "super_admin",].includes(user.role)
);
}
From 9bc5007e60633806cb235f87ea32b318c5aa3b83 Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Wed, 2 Jul 2025 18:37:45 -0600
Subject: [PATCH 10/13] removes super admin actions
---
apps/web/src/actions/admin/event-actions.ts | 2 +-
apps/web/src/lib/safe-action.ts | 11 -----------
2 files changed, 1 insertion(+), 12 deletions(-)
diff --git a/apps/web/src/actions/admin/event-actions.ts b/apps/web/src/actions/admin/event-actions.ts
index 34f9dbf6..24ee4b71 100644
--- a/apps/web/src/actions/admin/event-actions.ts
+++ b/apps/web/src/actions/admin/event-actions.ts
@@ -1,6 +1,6 @@
"use server";
-import { adminAction, superAdminAction } from "@/lib/safe-action";
+import { adminAction } from "@/lib/safe-action";
import { newEventFormSchema as editEventFormSchema } from "@/validators/event";
import { editEvent as modifyEvent } from "db/functions";
import { deleteEvent as removeEvent } from "db/functions";
diff --git a/apps/web/src/lib/safe-action.ts b/apps/web/src/lib/safe-action.ts
index 587fd51d..aa4c0532 100644
--- a/apps/web/src/lib/safe-action.ts
+++ b/apps/web/src/lib/safe-action.ts
@@ -49,14 +49,3 @@ export const adminAction = authenticatedAction.use(async ({ next, ctx }) => {
return next({ ctx: { user, ...ctx } });
});
-export const superAdminAction = authenticatedAction.use(
- async ({ next, ctx }) => {
- const user = await getUser(ctx.userId);
- if (!user || user.role !== "super_admin") {
- returnValidationErrors(z.null(), {
- _errors: ["Unauthorized (Not Super Admin)"],
- });
- }
- return next({ ctx: { user, ...ctx } });
- },
-);
From 68229736b6456b9fef56b366731c887c07c61166 Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Wed, 2 Jul 2025 18:55:02 -0600
Subject: [PATCH 11/13] updates event ui for volunteers
---
apps/web/src/app/admin/events/page.tsx | 17 +++++-
.../components/events/shared/EventColumns.tsx | 57 ++++++++++++-------
apps/web/src/lib/safe-action.ts | 3 +-
3 files changed, 53 insertions(+), 24 deletions(-)
diff --git a/apps/web/src/app/admin/events/page.tsx b/apps/web/src/app/admin/events/page.tsx
index f95b1612..2c1eeb12 100644
--- a/apps/web/src/app/admin/events/page.tsx
+++ b/apps/web/src/app/admin/events/page.tsx
@@ -7,6 +7,8 @@ import { PlusCircle } from "lucide-react";
import Link from "next/link";
import { getAllEvents, getUser } from "db/functions";
import { auth } from "@clerk/nextjs/server";
+import FullScreenMessage from "@/components/shared/FullScreenMessage";
+import { isUserAdmin } from "@/lib/utils/server/admin";
export default async function Page() {
const { userId, redirectToSignIn } = await auth();
@@ -15,10 +17,19 @@ export default async function Page() {
}
const userData = await getUser(userId);
- const isSuperAdmin = userData?.role === "super_admin";
+ if (!userData){
+ return (
+
+
+ )
+ }
+
const events = await getAllEvents();
-
+ const isUserAuthorized = isUserAdmin(userData);
return (
@@ -43,7 +54,7 @@ export default async function Page() {
({ ...ev, isSuperAdmin }))}
+ data={events.map((ev) => ({ ...ev, isUserAdmin:isUserAuthorized }))}
/>
);
diff --git a/apps/web/src/components/events/shared/EventColumns.tsx b/apps/web/src/components/events/shared/EventColumns.tsx
index 68061444..a447c1e9 100644
--- a/apps/web/src/components/events/shared/EventColumns.tsx
+++ b/apps/web/src/components/events/shared/EventColumns.tsx
@@ -31,8 +31,9 @@ import { useAction } from "next-safe-action/hooks";
import { deleteEventAction } from "@/actions/admin/event-actions";
import { toast } from "sonner";
import { LoaderCircle } from "lucide-react";
+import { error } from "console";
-type EventRow = eventTableValidatorType & { isSuperAdmin: boolean };
+type EventRow = eventTableValidatorType & { isUserAdmin: boolean };
export const columns: ColumnDef[] = [
{
@@ -78,6 +79,7 @@ export const columns: ColumnDef[] = [
accessorKey: "endTime",
header: "End",
cell: ({ row }) => (
+
{new Date(row.original.endTime).toLocaleDateString()}{" "}
{new Date(row.original.endTime).toLocaleTimeString("en-US", {
@@ -104,7 +106,19 @@ export const columns: ColumnDef[] = [
router.refresh();
setOpen(false);
},
- onError: (err) => {
+ onError: ({error:err}) => {
+ let description: string;
+
+ if (err.validationErrors?._errors) {
+ // User is not super admin
+ description = err.validationErrors._errors[0];
+ } else {
+ description =
+ err.serverError ||
+ "An unknown error occurred";
+ }
+
+ toast.error("Unable to edit event", { description });
toast.dismiss();
toast.error("Failed to delete event");
console.log(err);
@@ -144,26 +158,31 @@ export const columns: ColumnDef[] = [
-
-
- Edit
-
-
-
- Delete
-
+
+ Edit
+
+
+ )}
+ {row.original.isUserAdmin && (
+
+
+ Delete
+
+
+ )}
-
diff --git a/apps/web/src/lib/safe-action.ts b/apps/web/src/lib/safe-action.ts
index aa4c0532..d9c9b6f6 100644
--- a/apps/web/src/lib/safe-action.ts
+++ b/apps/web/src/lib/safe-action.ts
@@ -39,8 +39,7 @@ export const adminAction = authenticatedAction.use(async ({ next, ctx }) => {
const user = await getUser(ctx.userId);
if (
!user ||
- (user.role !== "admin" &&
- user.role !== "super_admin")
+ !isUserAdmin(user)
) {
returnValidationErrors(z.null(), {
_errors: ["Unauthorized (Not Admin)"],
From 84f8646c3e83fa933132c12ec3e534be3d841c30 Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Wed, 2 Jul 2025 18:55:21 -0600
Subject: [PATCH 12/13] formatter
---
apps/web/src/app/admin/events/page.tsx | 19 +++++------
apps/web/src/app/admin/layout.tsx | 2 +-
apps/web/src/app/admin/users/page.tsx | 4 +--
.../components/events/shared/EventColumns.tsx | 6 ++--
apps/web/src/lib/safe-action.ts | 32 +++++++++----------
apps/web/src/lib/utils/server/admin.ts | 4 +--
6 files changed, 31 insertions(+), 36 deletions(-)
diff --git a/apps/web/src/app/admin/events/page.tsx b/apps/web/src/app/admin/events/page.tsx
index 2c1eeb12..61ee1167 100644
--- a/apps/web/src/app/admin/events/page.tsx
+++ b/apps/web/src/app/admin/events/page.tsx
@@ -17,16 +17,14 @@ export default async function Page() {
}
const userData = await getUser(userId);
- if (!userData){
+ if (!userData) {
return (
-
-
- )
+
+ );
}
-
const events = await getAllEvents();
const isUserAuthorized = isUserAdmin(userData);
@@ -54,7 +52,10 @@ export default async function Page() {
({ ...ev, isUserAdmin:isUserAuthorized }))}
+ data={events.map((ev) => ({
+ ...ev,
+ isUserAdmin: isUserAuthorized,
+ }))}
/>
);
diff --git a/apps/web/src/app/admin/layout.tsx b/apps/web/src/app/admin/layout.tsx
index 78f1889b..47cd9a1f 100644
--- a/apps/web/src/app/admin/layout.tsx
+++ b/apps/web/src/app/admin/layout.tsx
@@ -86,7 +86,7 @@ export default async function AdminLayout({ children }: AdminLayoutProps) {
{Object.entries(c.dashPaths.admin).map(([name, path]) =>
- ["Users","Toggles"].includes(name) &&
+ ["Users", "Toggles"].includes(name) &&
user.role === "volunteer" ? null : (
),
diff --git a/apps/web/src/app/admin/users/page.tsx b/apps/web/src/app/admin/users/page.tsx
index 9cde884a..6e762102 100644
--- a/apps/web/src/app/admin/users/page.tsx
+++ b/apps/web/src/app/admin/users/page.tsx
@@ -13,9 +13,9 @@ import { isUserAdmin } from "@/lib/utils/server/admin";
// This begs a question where we might want to have an option later on to sort by the role as we might want different things
export default async function Page() {
const { userId } = await auth();
-
+
if (!userId) return notFound();
-
+
const admin = await getUser(userId);
if (!admin || !isUserAdmin(admin)) return notFound();
diff --git a/apps/web/src/components/events/shared/EventColumns.tsx b/apps/web/src/components/events/shared/EventColumns.tsx
index a447c1e9..fb6ed5a6 100644
--- a/apps/web/src/components/events/shared/EventColumns.tsx
+++ b/apps/web/src/components/events/shared/EventColumns.tsx
@@ -79,7 +79,6 @@ export const columns: ColumnDef[] = [
accessorKey: "endTime",
header: "End",
cell: ({ row }) => (
-
{new Date(row.original.endTime).toLocaleDateString()}{" "}
{new Date(row.original.endTime).toLocaleTimeString("en-US", {
@@ -106,7 +105,7 @@ export const columns: ColumnDef[] = [
router.refresh();
setOpen(false);
},
- onError: ({error:err}) => {
+ onError: ({ error: err }) => {
let description: string;
if (err.validationErrors?._errors) {
@@ -114,8 +113,7 @@ export const columns: ColumnDef[] = [
description = err.validationErrors._errors[0];
} else {
description =
- err.serverError ||
- "An unknown error occurred";
+ err.serverError || "An unknown error occurred";
}
toast.error("Unable to edit event", { description });
diff --git a/apps/web/src/lib/safe-action.ts b/apps/web/src/lib/safe-action.ts
index d9c9b6f6..822408a9 100644
--- a/apps/web/src/lib/safe-action.ts
+++ b/apps/web/src/lib/safe-action.ts
@@ -22,29 +22,27 @@ export const authenticatedAction = publicAction.use(
},
);
-export const volunteerAction = authenticatedAction.use(async ({ next, ctx }) => {
- const user = await getUser(ctx.userId);
- if (
- !user ||
- !["admin", "super_admin", "volunteer"].includes(user.role)
- ) {
- returnValidationErrors(z.null(), {
- _errors: ["Unauthorized (Not Admin)"],
- });
- }
- return next({ ctx: { user, ...ctx } });
-});
+export const volunteerAction = authenticatedAction.use(
+ async ({ next, ctx }) => {
+ const user = await getUser(ctx.userId);
+ if (
+ !user ||
+ !["admin", "super_admin", "volunteer"].includes(user.role)
+ ) {
+ returnValidationErrors(z.null(), {
+ _errors: ["Unauthorized (Not Admin)"],
+ });
+ }
+ return next({ ctx: { user, ...ctx } });
+ },
+);
export const adminAction = authenticatedAction.use(async ({ next, ctx }) => {
const user = await getUser(ctx.userId);
- if (
- !user ||
- !isUserAdmin(user)
- ) {
+ if (!user || !isUserAdmin(user)) {
returnValidationErrors(z.null(), {
_errors: ["Unauthorized (Not Admin)"],
});
}
return next({ ctx: { user, ...ctx } });
});
-
diff --git a/apps/web/src/lib/utils/server/admin.ts b/apps/web/src/lib/utils/server/admin.ts
index cd9b83db..d91062cb 100644
--- a/apps/web/src/lib/utils/server/admin.ts
+++ b/apps/web/src/lib/utils/server/admin.ts
@@ -1,7 +1,5 @@
import type { User } from "db/types";
export function isUserAdmin(user: User) {
- return (
- ["admin", "super_admin",].includes(user.role)
- );
+ return ["admin", "super_admin"].includes(user.role);
}
From a7aeb39b3eb501658d85921999a5de9d8c6d4a61 Mon Sep 17 00:00:00 2001
From: Christian Walker
Date: Tue, 8 Jul 2025 20:46:27 -0600
Subject: [PATCH 13/13] adds recent registrations
---
apps/web/src/app/admin/events/page.tsx | 18 +++---
apps/web/src/app/admin/page.tsx | 64 +++++++++++++++----
.../admin/toggles/RegistrationSettings.tsx | 5 +-
3 files changed, 66 insertions(+), 21 deletions(-)
diff --git a/apps/web/src/app/admin/events/page.tsx b/apps/web/src/app/admin/events/page.tsx
index 61ee1167..d43e3b4d 100644
--- a/apps/web/src/app/admin/events/page.tsx
+++ b/apps/web/src/app/admin/events/page.tsx
@@ -41,14 +41,16 @@ export default async function Page() {
-
+ {isUserAuthorized && (
+
+ )}
@@ -51,7 +62,6 @@ export default async function Page() {
{allUsers.length}
- {/* +20.1% from last month
*/}
@@ -63,7 +73,6 @@ export default async function Page() {
{0}
- {/* +20.1% from last month
*/}
@@ -75,7 +84,6 @@ export default async function Page() {
{rsvpCount}
- {/* +20.1% from last month
*/}
@@ -87,7 +95,6 @@ export default async function Page() {
{checkinCount}
- {/* +20.1% from last month
*/}
@@ -107,7 +114,6 @@ export default async function Page() {
days.
-
@@ -121,10 +127,32 @@ export default async function Page() {
Recent Registrations
{" "}
-
-
+
+
+ {recentRegisteredUsers.map((user) => (
+
+
+ {user.firstName} {user.lastName}
+
+
+ {formatInTimeZone(
+ user.signupTime,
+ timezone,
+ "MMMM dd h:mm a",
+ )}
+
+
+ ))}
+
+
@@ -136,6 +164,9 @@ function getRecentRegistrationData(users: User[]) {
let rsvpCount = 0;
let checkinCount = 0;
+
+ const recentRegisteredUsers: User[] = [];
+ let recentRegisteredUsersCount = 0;
let recentSignupCount: DateNumberMap = {};
for (let i = 0; i < 7; i++) {
@@ -156,10 +187,21 @@ function getRecentRegistrationData(users: User[]) {
const stamp = user.signupTime.toISOString().split("T")[0];
- if (recentSignupCount[stamp] != undefined) recentSignupCount[stamp]++;
+ if (recentSignupCount[stamp] != undefined) {
+ if (recentRegisteredUsersCount < 10) {
+ recentRegisteredUsers.push(user);
+ recentRegisteredUsersCount++;
+ }
+ recentSignupCount[stamp]++;
+ }
}
- return { rsvpCount, checkinCount, recentSignupCount };
+ return {
+ rsvpCount,
+ checkinCount,
+ recentSignupCount,
+ recentRegisteredUsers,
+ };
}
export const runtime = "edge";
diff --git a/apps/web/src/components/admin/toggles/RegistrationSettings.tsx b/apps/web/src/components/admin/toggles/RegistrationSettings.tsx
index 0b415fd6..bbf699ad 100644
--- a/apps/web/src/components/admin/toggles/RegistrationSettings.tsx
+++ b/apps/web/src/components/admin/toggles/RegistrationSettings.tsx
@@ -93,7 +93,8 @@ export function RegistrationToggles({
}}
/>
-
+ {/* removed until implemented */}
+ {/*
Allow Secret Code Sign-up
@@ -111,7 +112,7 @@ export function RegistrationToggles({
});
}}
/>
-
+
*/}