Skip to content
2 changes: 1 addition & 1 deletion apps/infrastructure-migrator/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/actions/admin/event-actions.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
8 changes: 4 additions & 4 deletions apps/web/src/actions/admin/scanner-admin-actions.ts
Original file line number Diff line number Diff line change
@@ -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(),
Expand Down Expand Up @@ -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 ({
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/actions/admin/user-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ 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!"],
Expand Down
36 changes: 25 additions & 11 deletions apps/web/src/app/admin/events/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -15,10 +17,17 @@ export default async function Page() {
}

const userData = await getUser(userId);
const isSuperAdmin = userData?.role === "super_admin";
if (!userData) {
return (
<FullScreenMessage
title="Access Denied"
message="You are not an admin. If you belive this is a mistake, please contact a administrator."
/>
);
}

const events = await getAllEvents();

const isUserAuthorized = isUserAdmin(userData);
return (
<div className="mx-auto max-w-7xl px-5 pt-44">
<div className="mb-5 grid w-full grid-cols-2">
Expand All @@ -32,18 +41,23 @@ export default async function Page() {
</p>
</div>
</div>
<div className="flex items-center justify-end">
<Link href="/admin/events/new">
<Button className="flex gap-x-1">
<PlusCircle />
New Event
</Button>
</Link>
</div>
{isUserAuthorized && (
<div className="flex items-center justify-end">
<Link href="/admin/events/new">
<Button className="flex gap-x-1">
<PlusCircle />
New Event
</Button>
</Link>
</div>
)}
</div>
<EventDataTable
columns={columns}
data={events.map((ev) => ({ ...ev, isSuperAdmin }))}
data={events.map((ev) => ({
...ev,
isUserAdmin: isUserAuthorized,
}))}
/>
</div>
);
Expand Down
16 changes: 12 additions & 4 deletions apps/web/src/app/admin/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<FullScreenMessage
Expand Down Expand Up @@ -80,9 +85,12 @@ export default async function AdminLayout({ children }: AdminLayoutProps) {
<div className="flex items-center justify-end gap-x-4 md:hidden"></div>
</div>
<div className="fixed z-20 mt-16 flex h-12 w-full border-b border-b-border bg-nav px-5">
{Object.entries(c.dashPaths.admin).map(([name, path]) => (
<DashNavItem key={name} name={name} path={path} />
))}
{Object.entries(c.dashPaths.admin).map(([name, path]) =>
["Users", "Toggles"].includes(name) &&
user.role === "volunteer" ? null : (
<DashNavItem key={name} name={name} path={path} />
),
)}
</div>
<Suspense fallback={<p>Loading...</p>}>{children}</Suspense>
</>
Expand Down
68 changes: 56 additions & 12 deletions apps/web/src/app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import type { User } from "db/types";
import { auth } from "@clerk/nextjs/server";
import { notFound } from "next/navigation";
import { getAllUsers, getUser } from "db/functions";
import Link from "next/link";
import { getRequestContext } from "@cloudflare/next-on-pages";
import { formatInTimeZone } from "date-fns-tz";
import { getClientTimeZone } from "@/lib/utils/client/shared";

export default async function Page() {
const { userId } = await auth();
Expand All @@ -19,15 +23,24 @@ export default async function Page() {
const adminUser = await getUser(userId);
if (
!adminUser ||
(adminUser.role !== "admin" && adminUser.role !== "super_admin")
(adminUser.role !== "admin" &&
adminUser.role !== "super_admin" &&
adminUser.role !== "volunteer")
) {
return notFound();
}

const allUsers = (await getAllUsers()) ?? [];

const { rsvpCount, checkinCount, recentSignupCount } =
getRecentRegistrationData(allUsers);
const {
rsvpCount,
checkinCount,
recentSignupCount,
recentRegisteredUsers,
} = getRecentRegistrationData(allUsers);
const { cf } = getRequestContext();

const timezone = getClientTimeZone(cf.timezone);

return (
<div className="mx-auto h-16 w-full max-w-7xl pt-44">
Expand All @@ -49,7 +62,6 @@ export default async function Page() {
<div className="text-2xl font-bold">
{allUsers.length}
</div>
{/* <p className="text-xs text-muted-foreground">+20.1% from last month</p> */}
</CardContent>
</Card>
<Card>
Expand All @@ -61,7 +73,6 @@ export default async function Page() {
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{0}</div>
{/* <p className="text-xs text-muted-foreground">+20.1% from last month</p> */}
</CardContent>
</Card>
<Card>
Expand All @@ -73,7 +84,6 @@ export default async function Page() {
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{rsvpCount}</div>
{/* <p className="text-xs text-muted-foreground">+20.1% from last month</p> */}
</CardContent>
</Card>
<Card>
Expand All @@ -85,7 +95,6 @@ export default async function Page() {
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{checkinCount}</div>
{/* <p className="text-xs text-muted-foreground">+20.1% from last month</p> */}
</CardContent>
</Card>
</div>
Expand All @@ -105,7 +114,6 @@ export default async function Page() {
days.
</CardDescription>
</div>

<User2 />
</CardHeader>
<CardContent>
Expand All @@ -119,10 +127,32 @@ export default async function Page() {
Recent Registrations
</CardTitle>{" "}
</div>

<TimerReset />
</CardHeader>
<CardContent></CardContent>
<CardContent>
<div className="flex flex-col space-y-2">
{recentRegisteredUsers.map((user) => (
<div
key={user.clerkID}
className="flex items-center justify-between"
>
<Link
href={`/admin/users/${user.clerkID}`}
className="hover:underline"
>
{user.firstName} {user.lastName}
</Link>
<span className="text-sm text-gray-500">
{formatInTimeZone(
user.signupTime,
timezone,
"MMMM dd h:mm a",
)}
</span>
</div>
))}
</div>
</CardContent>
</Card>
</div>
</div>
Expand All @@ -134,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++) {
Expand All @@ -154,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";
11 changes: 11 additions & 0 deletions apps/web/src/app/admin/users/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/app/api/upload/pfp/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ export async function POST(request: Request): Promise<NextResponse> {
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) {
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/app/api/upload/resume/register/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export async function POST(request: Request): Promise<NextResponse> {
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) {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/api/upload/resume/view/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export function RegistrationToggles({
}}
/>
</div>
<div className="flex items-center border-b border-b-muted py-4">
{/* removed until implemented */}
{/* <div className="flex items-center border-b border-b-muted py-4">
<p className="text-sm font-bold">
Allow Secret Code Sign-up
</p>
Expand All @@ -111,7 +112,7 @@ export function RegistrationToggles({
});
}}
/>
</div>
</div> */}
</div>
</div>
<div className="mt-5 rounded-lg border-2 border-muted px-5 py-10">
Expand Down
Loading