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
+