Skip to content

Commit 36fe615

Browse files
Feedback fixes (#185)
* updates to use bucket-env * fix volunteer permissions * fix bug with local storage write * debounce registration write * remove create registration alert * adds volunteer action * removes points * updates ui for volunteers * removes super admin actions * updates event ui for volunteers * adds recent registrations * formatter
1 parent e67fcfe commit 36fe615

File tree

20 files changed

+200
-91
lines changed

20 files changed

+200
-91
lines changed

apps/infrastructure-migrator/driver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ async function migratePostgresSqLite() {
240240

241241
const cmd = new PutObjectCommand({
242242
Key: key,
243-
Bucket: staticUploads.bucketName,
243+
Bucket: process.env.R2_BUCKET_NAME,
244244
ContentType: "application/pdf",
245245
///@ts-expect-error
246246
Body: buffer,

apps/web/src/actions/admin/event-actions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use server";
22

3-
import { adminAction, superAdminAction } from "@/lib/safe-action";
3+
import { adminAction } from "@/lib/safe-action";
44
import { newEventFormSchema as editEventFormSchema } from "@/validators/event";
55
import { editEvent as modifyEvent } from "db/functions";
66
import { deleteEvent as removeEvent } from "db/functions";

apps/web/src/actions/admin/scanner-admin-actions.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"use server";
22

3-
import { adminAction } from "@/lib/safe-action";
3+
import { volunteerAction } from "@/lib/safe-action";
44
import { z } from "zod";
55
import { db, sql } from "db";
66
import { scans, userCommonData } from "db/schema";
77
import { eq, and } from "db/drizzle";
88

9-
export const createScan = adminAction
9+
export const createScan = volunteerAction
1010
.schema(
1111
z.object({
1212
eventID: z.number(),
@@ -49,7 +49,7 @@ export const createScan = adminAction
4949
},
5050
);
5151

52-
export const getScan = adminAction
52+
export const getScan = volunteerAction
5353
.schema(z.object({ eventID: z.number(), userID: z.string() }))
5454
.action(
5555
async ({
@@ -77,7 +77,7 @@ const checkInUserSchema = z.object({
7777
}, "QR Code has expired. Please tell user refresh the QR Code"),
7878
});
7979

80-
export const checkInUserToHackathon = adminAction
80+
export const checkInUserToHackathon = volunteerAction
8181
.schema(checkInUserSchema)
8282
.action(async ({ parsedInput: { userID } }) => {
8383
// Set checkinTimestamp

apps/web/src/actions/admin/user-actions.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export const updateRole = adminAction
2323
}) => {
2424
if (
2525
user.role !== "super_admin" &&
26-
(roleToSet === "super_admin" || roleToSet === "admin")
26+
(roleToSet === "super_admin" ||
27+
roleToSet === "admin" ||
28+
roleToSet === "volunteer")
2729
) {
2830
returnValidationErrors(z.null(), {
2931
_errors: ["You are not allowed to do this!"],

apps/web/src/app/admin/events/page.tsx

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { PlusCircle } from "lucide-react";
77
import Link from "next/link";
88
import { getAllEvents, getUser } from "db/functions";
99
import { auth } from "@clerk/nextjs/server";
10+
import FullScreenMessage from "@/components/shared/FullScreenMessage";
11+
import { isUserAdmin } from "@/lib/utils/server/admin";
1012

1113
export default async function Page() {
1214
const { userId, redirectToSignIn } = await auth();
@@ -15,10 +17,17 @@ export default async function Page() {
1517
}
1618

1719
const userData = await getUser(userId);
18-
const isSuperAdmin = userData?.role === "super_admin";
20+
if (!userData) {
21+
return (
22+
<FullScreenMessage
23+
title="Access Denied"
24+
message="You are not an admin. If you belive this is a mistake, please contact a administrator."
25+
/>
26+
);
27+
}
1928

2029
const events = await getAllEvents();
21-
30+
const isUserAuthorized = isUserAdmin(userData);
2231
return (
2332
<div className="mx-auto max-w-7xl px-5 pt-44">
2433
<div className="mb-5 grid w-full grid-cols-2">
@@ -32,18 +41,23 @@ export default async function Page() {
3241
</p>
3342
</div>
3443
</div>
35-
<div className="flex items-center justify-end">
36-
<Link href="/admin/events/new">
37-
<Button className="flex gap-x-1">
38-
<PlusCircle />
39-
New Event
40-
</Button>
41-
</Link>
42-
</div>
44+
{isUserAuthorized && (
45+
<div className="flex items-center justify-end">
46+
<Link href="/admin/events/new">
47+
<Button className="flex gap-x-1">
48+
<PlusCircle />
49+
New Event
50+
</Button>
51+
</Link>
52+
</div>
53+
)}
4354
</div>
4455
<EventDataTable
4556
columns={columns}
46-
data={events.map((ev) => ({ ...ev, isSuperAdmin }))}
57+
data={events.map((ev) => ({
58+
...ev,
59+
isUserAdmin: isUserAuthorized,
60+
}))}
4761
/>
4862
</div>
4963
);

apps/web/src/app/admin/layout.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ export default async function AdminLayout({ children }: AdminLayoutProps) {
2424

2525
const user = await getUser(userId);
2626

27-
if (!user || (user.role !== "admin" && user.role !== "super_admin")) {
27+
if (
28+
!user ||
29+
(user.role !== "admin" &&
30+
user.role !== "super_admin" &&
31+
user.role !== "volunteer")
32+
) {
2833
console.log("Denying admin access to user", user);
2934
return (
3035
<FullScreenMessage
@@ -80,9 +85,12 @@ export default async function AdminLayout({ children }: AdminLayoutProps) {
8085
<div className="flex items-center justify-end gap-x-4 md:hidden"></div>
8186
</div>
8287
<div className="fixed z-20 mt-16 flex h-12 w-full border-b border-b-border bg-nav px-5">
83-
{Object.entries(c.dashPaths.admin).map(([name, path]) => (
84-
<DashNavItem key={name} name={name} path={path} />
85-
))}
88+
{Object.entries(c.dashPaths.admin).map(([name, path]) =>
89+
["Users", "Toggles"].includes(name) &&
90+
user.role === "volunteer" ? null : (
91+
<DashNavItem key={name} name={name} path={path} />
92+
),
93+
)}
8694
</div>
8795
<Suspense fallback={<p>Loading...</p>}>{children}</Suspense>
8896
</>

apps/web/src/app/admin/page.tsx

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import type { User } from "db/types";
1111
import { auth } from "@clerk/nextjs/server";
1212
import { notFound } from "next/navigation";
1313
import { getAllUsers, getUser } from "db/functions";
14+
import Link from "next/link";
15+
import { getRequestContext } from "@cloudflare/next-on-pages";
16+
import { formatInTimeZone } from "date-fns-tz";
17+
import { getClientTimeZone } from "@/lib/utils/client/shared";
1418

1519
export default async function Page() {
1620
const { userId } = await auth();
@@ -19,15 +23,24 @@ export default async function Page() {
1923
const adminUser = await getUser(userId);
2024
if (
2125
!adminUser ||
22-
(adminUser.role !== "admin" && adminUser.role !== "super_admin")
26+
(adminUser.role !== "admin" &&
27+
adminUser.role !== "super_admin" &&
28+
adminUser.role !== "volunteer")
2329
) {
2430
return notFound();
2531
}
2632

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

29-
const { rsvpCount, checkinCount, recentSignupCount } =
30-
getRecentRegistrationData(allUsers);
35+
const {
36+
rsvpCount,
37+
checkinCount,
38+
recentSignupCount,
39+
recentRegisteredUsers,
40+
} = getRecentRegistrationData(allUsers);
41+
const { cf } = getRequestContext();
42+
43+
const timezone = getClientTimeZone(cf.timezone);
3144

3245
return (
3346
<div className="mx-auto h-16 w-full max-w-7xl pt-44">
@@ -49,7 +62,6 @@ export default async function Page() {
4962
<div className="text-2xl font-bold">
5063
{allUsers.length}
5164
</div>
52-
{/* <p className="text-xs text-muted-foreground">+20.1% from last month</p> */}
5365
</CardContent>
5466
</Card>
5567
<Card>
@@ -61,7 +73,6 @@ export default async function Page() {
6173
</CardHeader>
6274
<CardContent>
6375
<div className="text-2xl font-bold">{0}</div>
64-
{/* <p className="text-xs text-muted-foreground">+20.1% from last month</p> */}
6576
</CardContent>
6677
</Card>
6778
<Card>
@@ -73,7 +84,6 @@ export default async function Page() {
7384
</CardHeader>
7485
<CardContent>
7586
<div className="text-2xl font-bold">{rsvpCount}</div>
76-
{/* <p className="text-xs text-muted-foreground">+20.1% from last month</p> */}
7787
</CardContent>
7888
</Card>
7989
<Card>
@@ -85,7 +95,6 @@ export default async function Page() {
8595
</CardHeader>
8696
<CardContent>
8797
<div className="text-2xl font-bold">{checkinCount}</div>
88-
{/* <p className="text-xs text-muted-foreground">+20.1% from last month</p> */}
8998
</CardContent>
9099
</Card>
91100
</div>
@@ -105,7 +114,6 @@ export default async function Page() {
105114
days.
106115
</CardDescription>
107116
</div>
108-
109117
<User2 />
110118
</CardHeader>
111119
<CardContent>
@@ -119,10 +127,32 @@ export default async function Page() {
119127
Recent Registrations
120128
</CardTitle>{" "}
121129
</div>
122-
123130
<TimerReset />
124131
</CardHeader>
125-
<CardContent></CardContent>
132+
<CardContent>
133+
<div className="flex flex-col space-y-2">
134+
{recentRegisteredUsers.map((user) => (
135+
<div
136+
key={user.clerkID}
137+
className="flex items-center justify-between"
138+
>
139+
<Link
140+
href={`/admin/users/${user.clerkID}`}
141+
className="hover:underline"
142+
>
143+
{user.firstName} {user.lastName}
144+
</Link>
145+
<span className="text-sm text-gray-500">
146+
{formatInTimeZone(
147+
user.signupTime,
148+
timezone,
149+
"MMMM dd h:mm a",
150+
)}
151+
</span>
152+
</div>
153+
))}
154+
</div>
155+
</CardContent>
126156
</Card>
127157
</div>
128158
</div>
@@ -134,6 +164,9 @@ function getRecentRegistrationData(users: User[]) {
134164

135165
let rsvpCount = 0;
136166
let checkinCount = 0;
167+
168+
const recentRegisteredUsers: User[] = [];
169+
let recentRegisteredUsersCount = 0;
137170
let recentSignupCount: DateNumberMap = {};
138171

139172
for (let i = 0; i < 7; i++) {
@@ -154,10 +187,21 @@ function getRecentRegistrationData(users: User[]) {
154187

155188
const stamp = user.signupTime.toISOString().split("T")[0];
156189

157-
if (recentSignupCount[stamp] != undefined) recentSignupCount[stamp]++;
190+
if (recentSignupCount[stamp] != undefined) {
191+
if (recentRegisteredUsersCount < 10) {
192+
recentRegisteredUsers.push(user);
193+
recentRegisteredUsersCount++;
194+
}
195+
recentSignupCount[stamp]++;
196+
}
158197
}
159198

160-
return { rsvpCount, checkinCount, recentSignupCount };
199+
return {
200+
rsvpCount,
201+
checkinCount,
202+
recentSignupCount,
203+
recentRegisteredUsers,
204+
};
161205
}
162206

163207
export const runtime = "edge";

apps/web/src/app/admin/users/page.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,20 @@ import { Button } from "@/components/shadcn/ui/button";
55
import { FolderInput } from "lucide-react";
66
import { getAllUsers } from "db/functions";
77
import { userCommonData } from "db/schema";
8+
import { getUser } from "db/functions";
9+
import { auth } from "@clerk/nextjs/server";
10+
import { notFound } from "next/navigation";
11+
import { isUserAdmin } from "@/lib/utils/server/admin";
812

913
// 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
1014
export default async function Page() {
15+
const { userId } = await auth();
16+
17+
if (!userId) return notFound();
18+
19+
const admin = await getUser(userId);
20+
if (!admin || !isUserAdmin(admin)) return notFound();
21+
1122
const userData = await getAllUsers();
1223

1324
return (

apps/web/src/app/api/upload/pfp/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ export async function POST(request: Request): Promise<NextResponse> {
2626
const randomSeq = crypto.randomUUID();
2727
const [fileName, extension] = body.fileName.split(".");
2828
const key = `${body.location}/${fileName}-${randomSeq}.${extension}`;
29-
const url = await getPresignedUploadUrl(staticUploads.bucketName, key);
29+
const url = await getPresignedUploadUrl(
30+
process.env.R2_BUCKET_NAME!,
31+
key,
32+
);
3033

3134
return NextResponse.json({ url, key });
3235
} catch (error) {

apps/web/src/app/api/upload/resume/register/route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ export async function POST(request: Request): Promise<NextResponse> {
2525
const randomSeq = crypto.randomUUID();
2626
const [fileName, extension] = body.fileName.split(".");
2727
const key = `${body.location}/${fileName}-${randomSeq}.${extension}`;
28-
const url = await getPresignedUploadUrl(staticUploads.bucketName, key);
28+
const url = await getPresignedUploadUrl(
29+
process.env.R2_BUCKET_NAME!,
30+
key,
31+
);
2932

3033
return NextResponse.json({ url, key });
3134
} catch (error) {

0 commit comments

Comments
 (0)