feat(frontend): Improve waitlist error display & messages (#11206)

Improves the "not on waitlist" error display based on feedback.

- Follow-up to #11198
  - Follow-up to #11196

### Changes 🏗️

- Use standard `ErrorCard`
- Improve text strings
- Merge `isWaitlistError` and `isWaitlistErrorFromParams`

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  <!-- Put your test plan here: -->
- [x] We need to test in dev becasue we don't have a waitlist locally
and will revert if it doesnt work
- deploy to dev environment and sign up with a non approved account and
see if error appears
This commit is contained in:
Nicholas Tindle
2025-10-22 08:37:42 -05:00
committed by GitHub
parent 39792d517e
commit e2a9923f30
6 changed files with 67 additions and 74 deletions

View File

@@ -1,12 +1,12 @@
"use client";
import { useEffect, useState } from "react";
import { Button } from "@/components/atoms/Button/Button";
import { Text } from "@/components/atoms/Text/Text";
import { Card } from "@/components/atoms/Card/Card";
import { WaitlistErrorContent } from "@/components/auth/WaitlistErrorContent";
import { isWaitlistErrorFromParams } from "@/app/api/auth/utils";
import { isWaitlistError } from "@/app/api/auth/utils";
import { useRouter } from "next/navigation";
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
import { environment } from "@/services/environment";
export default function AuthErrorPage() {
@@ -38,12 +38,9 @@ export default function AuthErrorPage() {
}
// Check if this is a waitlist/not allowed error
const isWaitlistError = isWaitlistErrorFromParams(
errorCode,
errorDescription,
);
const isWaitlistErr = isWaitlistError(errorCode, errorDescription);
if (isWaitlistError) {
if (isWaitlistErr) {
return (
<div className="flex h-screen items-center justify-center">
<Card className="w-full max-w-md p-8">
@@ -56,34 +53,25 @@ export default function AuthErrorPage() {
);
}
// Default error display for other types of errors
// Use ErrorCard for consistent error display
const errorMessage = errorDescription
? `${errorDescription}. If this error persists, please contact support at contact@agpt.co`
: "An authentication error occurred. Please contact support at contact@agpt.co";
return (
<div className="flex h-screen items-center justify-center">
<Card className="w-full max-w-md p-8">
<div className="flex flex-col items-center gap-6">
<Text variant="h3">Authentication Error</Text>
<div className="flex flex-col gap-2 text-center">
{errorType && (
<Text variant="body">
<strong>Error Type:</strong> {errorType}
</Text>
)}
{errorCode && (
<Text variant="body">
<strong>Error Code:</strong> {errorCode}
</Text>
)}
{errorDescription && (
<Text variant="body">
<strong>Description:</strong> {errorDescription}
</Text>
)}
</div>
<Button variant="primary" onClick={() => router.push("/login")}>
Back to Login
</Button>
</div>
</Card>
<div className="flex h-screen items-center justify-center p-4">
<div className="w-full max-w-md">
<ErrorCard
responseError={{
message: errorMessage,
detail: errorCode
? `Error code: ${errorCode}${errorType ? ` (${errorType})` : ""}`
: undefined,
}}
context="authentication"
onRetry={() => router.push("/login")}
/>
</div>
</div>
);
}

View File

@@ -59,6 +59,7 @@ export function useSignupPage() {
resetCaptcha();
return;
}
try {
const response = await fetch("/api/auth/provider", {
method: "POST",
@@ -71,7 +72,6 @@ export function useSignupPage() {
setIsGoogleLoading(false);
resetCaptcha();
// Check for waitlist error
if (error === "not_allowed") {
setShowNotAllowedModal(true);
return;
@@ -149,6 +149,7 @@ export function useSignupPage() {
setShowNotAllowedModal(true);
return;
}
toast({
title: result?.error || "Signup failed",
variant: "destructive",

View File

@@ -33,7 +33,7 @@ export async function POST(request: Request) {
if (error) {
// Check for waitlist/allowlist error
if (isWaitlistError(error)) {
if (isWaitlistError(error?.code, error?.message)) {
logWaitlistError("OAuth Provider", error.message);
return NextResponse.json({ error: "not_allowed" }, { status: 403 });
}

View File

@@ -30,6 +30,7 @@ export async function POST(request: Request) {
turnstileToken ?? "",
"signup",
);
if (!captchaOk) {
return NextResponse.json(
{ error: "CAPTCHA verification failed. Please try again." },
@@ -48,8 +49,7 @@ export async function POST(request: Request) {
const { data, error } = await supabase.auth.signUp(parsed.data);
if (error) {
// Check for waitlist/allowlist error
if (isWaitlistError(error)) {
if (isWaitlistError(error?.code, error?.message)) {
logWaitlistError("Signup", error.message);
return NextResponse.json({ error: "not_allowed" }, { status: 403 });
}

View File

@@ -1,49 +1,45 @@
/**
* Checks if a Supabase auth error is related to the waitlist/allowlist
* Checks if an error is related to the waitlist/allowlist
*
* Can be used with either:
* - Error objects from Supabase auth operations: `isWaitlistError(error?.code, error?.message)`
* - URL parameters from OAuth callbacks: `isWaitlistError(errorCode, errorDescription)`
*
* The PostgreSQL trigger raises P0001 with message format:
* "The email address "email" is not allowed to register. Please contact support for assistance."
*
* @param error - The error object from Supabase auth operations
* @returns true if this is a waitlist/allowlist error
*/
export function isWaitlistError(error: any): boolean {
if (!error?.message) return false;
if (error?.code === "P0001") return true;
return (
error.message.includes("P0001") || // PostgreSQL custom error code
error.message.includes("not allowed to register") || // Trigger message
error.message.toLowerCase().includes("allowed_users") // Table reference
);
}
/**
* Checks if OAuth callback URL parameters indicate a waitlist error
*
* This is for the auth-code-error page which receives errors via URL hash params
* from Supabase OAuth redirects
*
* @param errorCode - The error_code parameter from the URL
* @param errorDescription - The error_description parameter from the URL
* @param code - Error code (e.g., "P0001", "unexpected_failure") or null
* @param message - Error message/description or null
* @returns true if this appears to be a waitlist/allowlist error
*/
export function isWaitlistErrorFromParams(
errorCode?: string | null,
errorDescription?: string | null,
export function isWaitlistError(
code?: string | null,
message?: string | null,
): boolean {
if (!errorDescription) return false;
// Check for explicit PostgreSQL trigger error code
if (code === "P0001") return true;
if (errorCode === "P0001") return true;
if (!message) return false;
const description = errorDescription.toLowerCase();
const lowerMessage = message.toLowerCase();
// Check for the generic database error that occurs during waitlist check
// This happens when Supabase wraps the PostgreSQL trigger error
if (
code === "unexpected_failure" &&
message === "Database error saving new user"
) {
return true;
}
// Check for various waitlist-related patterns in the message
return (
description.includes("p0001") || // PostgreSQL error code might be in description
description.includes("not allowed") ||
description.includes("waitlist") ||
description.includes("allowlist") ||
description.includes("allowed_users")
lowerMessage.includes("p0001") || // PostgreSQL error code in message
lowerMessage.includes("not allowed") || // Common waitlist message
lowerMessage.includes("waitlist") || // Explicit waitlist mention
lowerMessage.includes("allowlist") || // Explicit allowlist mention
lowerMessage.includes("allowed_users") || // Database table reference
lowerMessage.includes("not allowed to register") // Full trigger message
);
}

View File

@@ -43,7 +43,15 @@ export function WaitlistErrorContent({
exact same email address you used when signing up.
</Text>
<Text variant="small" className="text-center text-muted-foreground">
If you&apos;re not sure which email you used or need help,{" "}
If you&apos;re not sure which email you used or need help, contact us
at{" "}
<a
href="mailto:contact@agpt.co"
className="underline hover:text-foreground"
>
contact@agpt.co
</a>{" "}
or{" "}
<a
href="https://discord.gg/autogpt"
target="_blank"