"use client"; import Image from "next/image"; import { useEffect, useRef, useState } from "react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { apiFetch } from "@/lib/api"; import PublicShell from "@/components/layout/PublicShell"; import { useDashboardI18n } from "@/components/I18nProvider"; import { buildHomepageSignInPath } from "@/lib/dashboardPaths"; import { websiteUrl } from "@/lib/urls"; const DASHBOARD_LOGO_SRC = "/dashboard/rentaldrivego.png"; const SUBSCRIPTION_PLANS = ["STARTER", "GROWTH", "PRO"] as const; type SubscriptionPlan = (typeof SUBSCRIPTION_PLANS)[number]; const PLAN_LABELS: Record = { STARTER: "Launch", GROWTH: "Growth", PRO: "Pro", }; function isSubscriptionPlan(value: string | null): value is SubscriptionPlan { return SUBSCRIPTION_PLANS.some((plan) => plan === value); } function notifyParent(message: Record) { if (typeof window === "undefined" || window.parent === window) return; window.parent.postMessage(message, "*"); } function withAuthQuery( params: { embedded: boolean; language: "en" | "fr" | "ar"; theme: string }, ) { const href = buildHomepageSignInPath(undefined, { locale: params.language, theme: params.theme, }); const url = new URL(href, "http://localhost"); if (params.embedded) url.searchParams.set("embedded", "1"); return `${url.pathname}${url.search}`; } export default function SignUpForm({ embedded = false, }: { embedded?: boolean; }) { const { language, theme, setLanguage, setTheme } = useDashboardI18n(); const pathname = usePathname(); const router = useRouter(); const searchParams = useSearchParams(); const requestedLanguage = searchParams.get("lang"); const requestedTheme = searchParams.get("theme"); const requestedPlan = searchParams.get("plan"); const selectedPlan = isSubscriptionPlan(requestedPlan) ? requestedPlan : null; const initializedFromQuery = useRef(false); const initialLanguage = requestedLanguage === "en" || requestedLanguage === "fr" || requestedLanguage === "ar" ? requestedLanguage : language; const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [companyName, setCompanyName] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [preferredLanguage, setPreferredLanguage] = useState< "en" | "fr" | "ar" >(initialLanguage); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); useEffect(() => { if (initializedFromQuery.current) return; if ( (requestedLanguage === "en" || requestedLanguage === "fr" || requestedLanguage === "ar") && requestedLanguage !== language ) { setLanguage(requestedLanguage); setPreferredLanguage(requestedLanguage); } if ( (requestedTheme === "light" || requestedTheme === "dark") && requestedTheme !== theme ) { setTheme(requestedTheme); } initializedFromQuery.current = true; }, [ language, requestedLanguage, requestedTheme, setLanguage, setTheme, theme, ]); useEffect(() => { if (!initializedFromQuery.current) return; if (language !== preferredLanguage) setPreferredLanguage(language); }, [language, preferredLanguage]); useEffect(() => { if (!initializedFromQuery.current) return; const params = new URLSearchParams(searchParams.toString()); let changed = false; if (params.get("lang") !== preferredLanguage) { params.set("lang", preferredLanguage); changed = true; } if (params.get("theme") !== theme) { params.set("theme", theme); changed = true; } if (embedded && params.get("embedded") !== "1") { params.set("embedded", "1"); changed = true; } if (!changed) return; const nextQuery = params.toString(); router.replace(nextQuery ? `${pathname}?${nextQuery}` : pathname, { scroll: false, }); }, [embedded, pathname, preferredLanguage, router, searchParams, theme]); useEffect(() => { const currentPath = window.location.pathname + (window.location.search || ""); notifyParent({ type: "rentaldrivego:embedded-path", path: currentPath }); }, [pathname, searchParams]); const dict = { en: { title: "Create account", subtitle: "Enter your details to create your workspace.", firstName: "First name", firstNamePlaceholder: "Aya", lastName: "Last name", lastNamePlaceholder: "Benali", company: "Company", companyPlaceholder: "Atlas Cars", email: "Email", emailPlaceholder: "you@company.com", password: "Password", passwordPlaceholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", confirmPassword: "Confirm password", passwordHint: "Minimum 8 characters", languageLabel: "Preferred language", create: "Create account", subscribeToPlan: "Subscribe to {plan}", creating: "Creating\u2026", alreadyHave: "Already have an account?", signIn: "Sign in", errorEmailTaken: "An account with this email already exists.", errorPasswordMismatch: "Passwords do not match.", errorGeneric: "Something went wrong. Please try again.", successTitle: "Check your email", successMessage: "We sent a verification link to {email}. Click the link to activate your account, then sign in.", successSignIn: "Go to sign in", }, fr: { title: "Cr\u00e9er un compte", subtitle: "Saisissez vos informations pour cr\u00e9er votre espace.", firstName: "Pr\u00e9nom", firstNamePlaceholder: "Aya", lastName: "Nom", lastNamePlaceholder: "Benali", company: "Entreprise", companyPlaceholder: "Atlas Cars", email: "Email", emailPlaceholder: "vous@entreprise.com", password: "Mot de passe", passwordPlaceholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", confirmPassword: "Confirmer le mot de passe", passwordHint: "Minimum 8 caract\u00e8res", languageLabel: "Langue pr\u00e9f\u00e9r\u00e9e", create: "Cr\u00e9er un compte", subscribeToPlan: "S’abonner à {plan}", creating: "Cr\u00e9ation\u2026", alreadyHave: "Vous avez d\u00e9j\u00e0 un compte ?", signIn: "Se connecter", errorEmailTaken: "Un compte avec cet email existe d\u00e9j\u00e0.", errorPasswordMismatch: "Les mots de passe ne correspondent pas.", errorGeneric: "Une erreur est survenue. Veuillez r\u00e9essayer.", successTitle: "V\u00e9rifiez votre email", successMessage: "Nous avons envoy\u00e9 un lien de v\u00e9rification \u00e0 {email}. Cliquez sur le lien pour activer votre compte, puis connectez-vous.", successSignIn: "Aller \u00e0 la connexion", }, ar: { title: "\u0623\u0646\u0634\u0626 \u062d\u0633\u0627\u0628\u064b\u0627", subtitle: "\u0623\u062f\u062e\u0644 \u0628\u064a\u0627\u0646\u0627\u062a\u0643 \u0644\u0625\u0646\u0634\u0627\u0621 \u0645\u0633\u0627\u062d\u0629 \u0639\u0645\u0644\u0643.", firstName: "\u0627\u0644\u0627\u0633\u0645 \u0627\u0644\u0623\u0648\u0644", firstNamePlaceholder: "Aya", lastName: "\u0627\u0633\u0645 \u0627\u0644\u0639\u0627\u0626\u0644\u0629", lastNamePlaceholder: "Benali", company: "\u0627\u0644\u0634\u0631\u0643\u0629", companyPlaceholder: "Atlas Cars", email: "\u0627\u0644\u0628\u0631\u064a\u062f \u0627\u0644\u0625\u0644\u0643\u062a\u0631\u0648\u0646\u064a", emailPlaceholder: "you@company.com", password: "\u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631", passwordPlaceholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", confirmPassword: "\u062a\u0623\u0643\u064a\u062f \u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631", passwordHint: "\u0627\u0644\u062d\u062f \u0627\u0644\u0623\u062f\u0646\u0649 8 \u0623\u062d\u0631\u0641", languageLabel: "\u0627\u0644\u0644\u063a\u0629 \u0627\u0644\u0645\u0641\u0636\u0644\u0629", create: "\u0623\u0646\u0634\u0626 \u062d\u0633\u0627\u0628\u064b\u0627", subscribeToPlan: "\u0627\u0634\u062a\u0631\u0643 \u0641\u064a {plan}", creating: "\u062c\u0627\u0631\u064d \u0627\u0644\u0625\u0646\u0634\u0627\u0621\u2026", alreadyHave: "\u0647\u0644 \u0644\u062f\u064a\u0643 \u062d\u0633\u0627\u0628 \u0628\u0627\u0644\u0641\u0639\u0644\u061f", signIn: "\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644", errorEmailTaken: "\u064a\u0648\u062c\u062f \u062d\u0633\u0627\u0628 \u0628\u0647\u0630\u0627 \u0627\u0644\u0628\u0631\u064a\u062f \u0627\u0644\u0625\u0644\u0643\u062a\u0631\u0648\u0646\u064a \u0628\u0627\u0644\u0641\u0639\u0644.", errorPasswordMismatch: "\u0643\u0644\u0645\u062a\u0627 \u0627\u0644\u0645\u0631\u0648\u0631 \u063a\u064a\u0631 \u0645\u062a\u0637\u0627\u0628\u0642\u062a\u064a\u0646.", errorGeneric: "\u062d\u062f\u062b \u062e\u0637\u0623 \u0645\u0627. \u064a\u0631\u062c\u0649 \u0627\u0644\u0645\u062d\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062e\u0631\u0649.", successTitle: "\u062a\u062d\u0642\u0642 \u0645\u0646 \u0628\u0631\u064a\u062f\u0643 \u0627\u0644\u0625\u0644\u0643\u062a\u0631\u0648\u0646\u064a", successMessage: "\u0623\u0631\u0633\u0644\u0646\u0627 \u0631\u0627\u0628\u0637 \u062a\u062d\u0642\u0642 \u0625\u0644\u0649 {email}. \u0627\u0646\u0642\u0631 \u0639\u0644\u0649 \u0627\u0644\u0631\u0627\u0628\u0637 \u0644\u062a\u0641\u0639\u064a\u0644 \u062d\u0633\u0627\u0628\u0643\u060c \u062b\u0645 \u0642\u0645 \u0628\u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644.", successSignIn: "\u0627\u0644\u0630\u0647\u0627\u0628 \u0625\u0644\u0649 \u062a\u0633\u062c\u064a\u0644 \u0627\u0644\u062f\u062e\u0648\u0644", }, }[preferredLanguage]; const signInHref = withAuthQuery({ embedded, language: preferredLanguage, theme, }); const submitLabel = selectedPlan ? dict.subscribeToPlan.replace("{plan}", PLAN_LABELS[selectedPlan]) : dict.create; const textPattern = { en: "[A-Za-z\\s'-]+", fr: "[A-Za-zÀ-ÖØ-öø-ÿŒœ\\s'-]+", ar: "[\\u0600-\\u06FF\\u0750-\\u077F\\s،؛؟ـ'-]+", }[preferredLanguage]; async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (password !== confirmPassword) { setError(dict.errorPasswordMismatch); return; } setLoading(true); setError(null); try { await apiFetch("/auth/account/start", { method: "POST", body: JSON.stringify({ firstName: firstName.trim(), lastName: lastName.trim(), companyName: companyName.trim(), email: email.trim(), password, preferredLanguage, ...(selectedPlan ? { subscriptionPlan: selectedPlan } : {}), }), }); setLanguage(preferredLanguage); document.cookie = `rentaldrivego-language=${preferredLanguage}; path=/; max-age=31536000; samesite=lax`; setSuccess(true); } catch (err: any) { if ( err?.message?.includes("email_taken") || err?.code === "email_taken" ) { setError(dict.errorEmailTaken); } else { setError(dict.errorGeneric); } } finally { setLoading(false); } } if (success) { return (
RentalDriveGo

{dict.successTitle}

{dict.successMessage.replace("{email}", email)}

{dict.successSignIn}
); } return (

{dict.title}

{dict.subtitle}

{error ? (
{error}
) : null}
setFirstName(e.target.value)} placeholder={dict.firstNamePlaceholder} autoFocus dir={preferredLanguage === "ar" ? "rtl" : "ltr"} pattern={textPattern} className="w-full rounded-2xl border border-stone-200 bg-white px-4 py-3 text-sm text-stone-900 transition-colors placeholder:text-stone-400 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-blue-800 dark:bg-blue-950/80 dark:text-stone-100 dark:placeholder:text-stone-500" />
setLastName(e.target.value)} placeholder={dict.lastNamePlaceholder} dir={preferredLanguage === "ar" ? "rtl" : "ltr"} pattern={textPattern} className="w-full rounded-2xl border border-stone-200 bg-white px-4 py-3 text-sm text-stone-900 transition-colors placeholder:text-stone-400 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-blue-800 dark:bg-blue-950/80 dark:text-stone-100 dark:placeholder:text-stone-500" />
setCompanyName(e.target.value)} placeholder={dict.companyPlaceholder} className="w-full rounded-2xl border border-stone-200 bg-white px-4 py-3 text-sm text-stone-900 transition-colors placeholder:text-stone-400 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-blue-800 dark:bg-blue-950/80 dark:text-stone-100 dark:placeholder:text-stone-500" />
setEmail(e.target.value)} placeholder={dict.emailPlaceholder} className="w-full rounded-2xl border border-stone-200 bg-white px-4 py-3 text-sm text-stone-900 transition-colors placeholder:text-stone-400 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-blue-800 dark:bg-blue-950/80 dark:text-stone-100 dark:placeholder:text-stone-500" />
setPassword(e.target.value)} placeholder={ "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" } className="w-full rounded-2xl border border-stone-200 bg-white px-4 py-3 pr-10 text-sm text-stone-900 transition-colors placeholder:text-stone-400 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-blue-800 dark:bg-blue-950/80 dark:text-stone-100 dark:placeholder:text-stone-500" />

{dict.passwordHint}

setConfirmPassword(e.target.value)} placeholder={dict.passwordPlaceholder} className="w-full rounded-2xl border border-stone-200 bg-white px-4 py-3 text-sm text-stone-900 transition-colors placeholder:text-stone-400 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-orange-500 dark:border-blue-800 dark:bg-blue-950/80 dark:text-stone-100 dark:placeholder:text-stone-500" />

{dict.alreadyHave}{" "} {dict.signIn}

); }