"use client"; import { useAtom } from "jotai"; import { AnimatePresence, motion } from "motion/react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { useEffect, useState } from "react"; import { toast, type ExternalToast } from "sonner"; import { registerMutationAtom } from "@/atoms/auth/auth-mutation.atoms"; import { Logo } from "@/components/Logo"; import { Spinner } from "@/components/ui/spinner"; import { getAuthErrorDetails, isNetworkError, shouldRetry } from "@/lib/auth-errors"; import { AUTH_TYPE } from "@/lib/env-config"; import { AppError, ValidationError } from "@/lib/error"; import { trackRegistrationAttempt, trackRegistrationFailure, trackRegistrationSuccess, } from "@/lib/posthog/events"; import { AmbientBackground } from "../login/AmbientBackground"; export default function RegisterPage() { const t = useTranslations("auth"); const tCommon = useTranslations("common"); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [error, setError] = useState<{ title: string | null; message: string | null; }>({ title: null, message: null, }); const router = useRouter(); const [{ mutateAsync: register, isPending: isRegistering }] = useAtom(registerMutationAtom); // Check authentication type and redirect if not LOCAL useEffect(() => { if (AUTH_TYPE !== "LOCAL") { router.push("/login"); } }, [router]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); // Form validation if (password !== confirmPassword) { setError({ title: t("password_mismatch"), message: t("passwords_no_match_desc") }); toast.error(t("password_mismatch"), { description: t("passwords_no_match_desc"), duration: 4000, }); return; } setError({ title: null, message: null }); // Clear any previous errors // Track registration attempt trackRegistrationAttempt(); try { await register({ email, password, is_active: true, is_superuser: false, is_verified: false, }); // Track successful registration trackRegistrationSuccess(); // Success toast toast.success(t("register_success"), { description: t("redirecting_login"), duration: 2000, }); // Small delay to show success message setTimeout(() => { router.push("/login?registered=true"); }, 500); } catch (err) { if (err instanceof AppError) { switch (err.status) { case 403: { const friendlyMessage = "Registrations are currently closed. If you need access, contact your administrator."; trackRegistrationFailure("Registration disabled"); setError({ title: "Registration is disabled", message: friendlyMessage }); toast.error("Registration is disabled", { description: friendlyMessage, duration: 6000, }); return; } default: break; } if (err instanceof ValidationError) { trackRegistrationFailure(err.message); setError({ title: err.name, message: err.message }); toast.error(err.name, { description: err.message, duration: 6000, }); return; } } // Use auth-errors utility to get proper error details let errorCode = "UNKNOWN_ERROR"; if (err instanceof Error) { errorCode = err.message; } else if (isNetworkError(err)) { errorCode = "NETWORK_ERROR"; } // Track registration failure trackRegistrationFailure(errorCode); // Get detailed error information from auth-errors utility const errorDetails = getAuthErrorDetails(errorCode); // Set persistent error display setError({ title: errorDetails.title, message: errorDetails.description }); // Show error toast with conditional retry action const toastOptions: ExternalToast = { description: errorDetails.description, duration: 6000, }; // Add retry action if the error is retryable if (shouldRetry(errorCode)) { toastOptions.action = { label: tCommon("retry"), onClick: () => handleSubmit(e), }; } toast.error(errorDetails.title, toastOptions); } }; return (

{t("create_account")}

{/* Enhanced Error Display */} {error?.title && (
Error Icon

{error.title}

{error.message}

)}
setEmail(e.target.value)} className={`mt-1 block w-full rounded-md border px-3 py-1.5 md:py-2 shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 dark:bg-gray-800 dark:text-white transition-all ${ error.title ? "border-red-300 focus:border-red-500 focus:ring-red-500 dark:border-red-700" : "border-gray-300 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-700" }`} disabled={isRegistering} />
setPassword(e.target.value)} className={`mt-1 block w-full rounded-md border px-3 py-1.5 md:py-2 shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 dark:bg-gray-800 dark:text-white transition-all ${ error.title ? "border-red-300 focus:border-red-500 focus:ring-red-500 dark:border-red-700" : "border-gray-300 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-700" }`} disabled={isRegistering} />
setConfirmPassword(e.target.value)} className={`mt-1 block w-full rounded-md border px-3 py-1.5 md:py-2 shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 dark:bg-gray-800 dark:text-white transition-all ${ error.title ? "border-red-300 focus:border-red-500 focus:ring-red-500 dark:border-red-700" : "border-gray-300 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-700" }`} disabled={isRegistering} />

{t("already_have_account")}{" "} {t("sign_in")}

); }