From 2750867667d1c0dc6d5d781ff8f74fe7cf5303f4 Mon Sep 17 00:00:00 2001
From: Matthew <92887765+mjanderson1227@users.noreply.github.com>
Date: Thu, 11 Jul 2024 22:50:08 -0500
Subject: [PATCH] Added a maximum limit of users who can RSVP * Added default
limit for the number of allowed RSVPs in the config. * Added check to prevent
RSVP users from going over the allowed limit in the RSVP page. * Added the
ability to edit RSVPLimit from the admin dashboard.
---
.../src/actions/admin/registration-actions.ts | 13 ++++
.../app/admin/toggles/registration/page.tsx | 17 +++--
apps/web/src/app/rsvp/page.tsx | 22 ++++---
.../admin/toggles/RegistrationSettings.tsx | 27 ++++++++
.../toggles/UpdateItemWithConfirmation.tsx | 66 +++++++++++++++++++
apps/web/src/lib/utils/server/redis.ts | 8 +++
packages/config/hackkit.config.ts | 1 +
7 files changed, 139 insertions(+), 15 deletions(-)
create mode 100644 apps/web/src/components/admin/toggles/UpdateItemWithConfirmation.tsx
diff --git a/apps/web/src/actions/admin/registration-actions.ts b/apps/web/src/actions/admin/registration-actions.ts
index 9388bbba..8a953100 100644
--- a/apps/web/src/actions/admin/registration-actions.ts
+++ b/apps/web/src/actions/admin/registration-actions.ts
@@ -9,6 +9,10 @@ const defaultRegistrationToggleSchema = z.object({
enabled: z.boolean(),
});
+const defaultRSVPLimitSchema = z.object({
+ rsvpLimit: z.number()
+});
+
export const toggleRegistrationEnabled = adminAction(
defaultRegistrationToggleSchema,
async ({ enabled }, { user, userId }) => {
@@ -44,3 +48,12 @@ export const toggleRSVPs = adminAction(
return { success: true, statusSet: enabled };
},
);
+
+export const setRSVPLimit = adminAction(
+ defaultRSVPLimitSchema,
+ async ({ rsvpLimit }) => {
+ await kv.set("config:registration:maxRSVPs", rsvpLimit);
+ revalidatePath("/admin/toggles/registration");
+ return { success: true, statusSet: rsvpLimit };
+ }
+);
diff --git a/apps/web/src/app/admin/toggles/registration/page.tsx b/apps/web/src/app/admin/toggles/registration/page.tsx
index 3825236b..2b990ed6 100644
--- a/apps/web/src/app/admin/toggles/registration/page.tsx
+++ b/apps/web/src/app/admin/toggles/registration/page.tsx
@@ -1,6 +1,7 @@
import { RegistrationToggles } from "@/components/admin/toggles/RegistrationSettings";
import { kv } from "@vercel/kv";
-import { parseRedisBoolean } from "@/lib/utils/server/redis";
+import { parseRedisBoolean, parseRedisNumber } from "@/lib/utils/server/redis";
+import c from "config";
export default async function Page() {
const pipe = kv.pipeline();
@@ -8,14 +9,14 @@ 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, defaultRSVPLimit]: (
+ | string
+ | null
+ )[] = await kv.mget(
"config:registration:registrationEnabled",
"config:registration:secretRegistrationEnabled",
"config:registration:allowRSVPs",
+ "config:registration:maxRSVPs"
);
return (
@@ -38,6 +39,10 @@ export default async function Page() {
defaultRSVPsEnabled,
true,
)}
+ defaultRSVPLimit={parseRedisNumber(
+ defaultRSVPLimit,
+ c.rsvpDefaultLimit
+ )}
/>
);
diff --git a/apps/web/src/app/rsvp/page.tsx b/apps/web/src/app/rsvp/page.tsx
index 0bd5d2cd..320a7ea4 100644
--- a/apps/web/src/app/rsvp/page.tsx
+++ b/apps/web/src/app/rsvp/page.tsx
@@ -2,13 +2,13 @@ import ConfirmDialogue from "@/components/rsvp/ConfirmDialogue";
import c from "config";
import { auth } from "@clerk/nextjs";
import { redirect } from "next/navigation";
-import { db } from "db";
+import { count, db } from "db";
import { eq } from "db/drizzle";
import { users } from "db/schema";
import ClientToast from "@/components/shared/ClientToast";
import { SignedOut, RedirectToSignIn } from "@clerk/nextjs";
import { kv } from "@vercel/kv";
-import { parseRedisBoolean } from "@/lib/utils/server/redis";
+import { parseRedisBoolean, parseRedisNumber } from "@/lib/utils/server/redis";
import Link from "next/link";
import { Button } from "@/components/shadcn/ui/button";
import { CheckCircleIcon } from "lucide-react";
@@ -38,15 +38,19 @@ export default async function RsvpPage({
}
const rsvpEnabled = await kv.get("config:registration:allowRSVPs");
+ const rsvpLimit = parseRedisNumber(await kv.get("config:registration:maxRSVPs"), c.rsvpDefaultLimit);
+ const rsvpUserCount = await db
+ .select({ count: count() })
+ .from(users)
+ .where(eq(users.rsvp, true))
+ .limit(rsvpLimit)
+ .then((result) => result[0].count);
// TODO: fix type jank here
- if (
- parseRedisBoolean(
- rsvpEnabled as string | boolean | null | undefined,
- true,
- ) === true ||
- user.rsvp === true
- ) {
+ const isRsvpPossible = parseRedisBoolean(rsvpEnabled as string | boolean | null | undefined, true) === true &&
+ rsvpUserCount < rsvpLimit;
+
+ if (isRsvpPossible || user.rsvp === true) {
return (
<>
RSVP Limit
+