import { A, useNavigate } from "@solidjs/router"; import { createMemo, createSignal, For, Show } from "solid-js"; import { useAuth } from "~/lib/auth"; import PublicBackground from "~/components/PublicBackground"; import PublicHeader from "~/components/PublicHeader"; import CaptchaCanvas from "~/components/CaptchaCanvas"; import { isValidEmail } from "~/lib/form-validation"; type RoleKey = "company" | "job_seeker" | "professional" | "customer"; function normalizeRoleValue(value: unknown): string { return String(value || "") .trim() .toUpperCase() .replace(/\s+/g, "_"); } function getStoredPreferredRole(emailHint?: string): string | null { if (typeof window === "undefined") return null; const keys = ["nxtgauge_signup_profile_v1", "nxtgauge_auth_user", "nxtgauge_user"]; for (const key of keys) { const raw = window.localStorage.getItem(key); if (!raw) continue; try { const parsed = JSON.parse(raw) as Record; const storedEmail = String(parsed?.email || "") .trim() .toLowerCase(); if (emailHint && storedEmail && storedEmail !== emailHint.trim().toLowerCase()) continue; const selectedProfessionalRole = normalizeRoleValue(parsed?.selectedProfessionalRole); if (selectedProfessionalRole) return selectedProfessionalRole; const activeRole = normalizeRoleValue(parsed?.active_role || parsed?.role); if (activeRole) return activeRole; } catch { // Ignore malformed local storage payloads. } } return null; } function resolveActiveRole(rawBackendRole: unknown, emailHint?: string): string { const backendRole = normalizeRoleValue(rawBackendRole); if (backendRole) return backendRole; const preferredRole = getStoredPreferredRole(emailHint); if (preferredRole) return preferredRole; return preferredRole || "JOB_SEEKER"; } function makeCaptcha() { const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; return Array.from({ length: 6 }, () => chars[Math.floor(Math.random() * chars.length)]).join(""); } function PasswordVisibilityIcon(props: { visible: boolean }) { if (props.visible) { return ( ); } return ( ); } export default function LoginRoute() { const navigate = useNavigate(); const auth = useAuth(); const [email, setEmail] = createSignal(""); const [password, setPassword] = createSignal(""); const [otp, setOtp] = createSignal(["", "", "", "", "", ""]); const [showVerify, setShowVerify] = createSignal(false); const [showPassword, setShowPassword] = createSignal(false); const [captcha, setCaptcha] = createSignal(makeCaptcha()); const [captchaInput, setCaptchaInput] = createSignal(""); const [error, setError] = createSignal(""); const [submitting, setSubmitting] = createSignal(false); const [roleGuess, setRoleGuess] = createSignal("job_seeker"); const [roleHint, setRoleHint] = createSignal(""); const [checkingRole, setCheckingRole] = createSignal(false); const otpCode = createMemo(() => otp().join("")); const formatRoleLabel = (value: string): string => String(value || "") .trim() .replace(/[_\s]+/g, " ") .toLowerCase() .replace(/\b\w/g, (ch) => ch.toUpperCase()); const lookupRoleByEmail = async (emailValue: string) => { const normalized = emailValue.trim().toLowerCase(); if (!normalized || !isValidEmail(normalized)) { setRoleHint(""); return; } setCheckingRole(true); try { const response = await fetch("/api/gateway/api/auth/check-email", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, credentials: "include", body: JSON.stringify({ email: normalized }), }); const payload = await response.json().catch(() => ({})); if (!response.ok || !payload?.exists) { setRoleHint(""); return; } const detectedRole = normalizeRoleValue( payload?.active_role || payload?.role || payload?.roles?.[0] ); if (!detectedRole) { const fallbackRole = normalizeRoleValue(getStoredPreferredRole(normalized)); setRoleHint(fallbackRole ? `Role: ${formatRoleLabel(fallbackRole)}` : "Role: Not assigned"); return; } setRoleHint(`Role: ${formatRoleLabel(detectedRole)}`); const roleLower = detectedRole.toLowerCase(); if ( roleLower === "company" || roleLower === "customer" || roleLower === "job_seeker" || roleLower === "professional" ) { setRoleGuess(roleLower as RoleKey); } } catch { setRoleHint(""); } finally { setCheckingRole(false); } }; const setOtpDigit = (index: number, value: string) => { const clean = value.replace(/\D/g, "").slice(0, 1); setOtp((prev) => { const next = prev.slice(); next[index] = clean; return next; }); if (clean) { const nextEl = document.querySelector(`#login-otp-${index + 1}`); if (nextEl) nextEl.focus(); } }; const saveUser = (user: any) => { const fullName = String(user?.full_name || user?.fullName || "").trim(); const [firstName, ...rest] = fullName.split(" "); const lastName = rest.join(" "); const normalizedRole = resolveActiveRole( user?.active_role || user?.role || roleGuess(), String(user?.email || email()) ); const storedRole = normalizedRole ? normalizedRole.toLowerCase() : roleGuess(); const selectedProfessionalRole = getStoredPreferredRole(String(user?.email || email())); const payload = { firstName: firstName || "", lastName: lastName || "", fullName: fullName || "", name: fullName || "", displayName: fullName || "", email: String(user?.email || email()) .trim() .toLowerCase(), roleKey: storedRole, role: storedRole, active_role: normalizedRole || "JOB_SEEKER", selectedProfessionalRole: selectedProfessionalRole || null, user, }; if (typeof window !== "undefined") { window.localStorage.setItem("nxtgauge_auth_user", JSON.stringify(payload)); window.localStorage.setItem("nxtgauge_user", JSON.stringify(payload)); window.localStorage.setItem("nxtgauge_signup_profile_v1", JSON.stringify(payload)); } }; const login = async () => { if (submitting()) return; setError(""); if (!isValidEmail(email())) { setError("Enter a valid email address."); return; } if (!password().trim()) { setError("Password is required."); return; } if (!captchaInput().trim() || captchaInput().trim().toUpperCase() !== captcha().toUpperCase()) { setError("Captcha does not match. Please try again."); setCaptcha(makeCaptcha()); setCaptchaInput(""); return; } setSubmitting(true); try { const res = await fetch("/api/gateway/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, credentials: "include", body: JSON.stringify({ email: email().trim().toLowerCase(), password: password(), }), }); const data = await res.json().catch(() => ({})); if (!res.ok) { const code = String(data?.code || "").toUpperCase(); if (code === "EMAIL_NOT_VERIFIED") { setShowVerify(true); setError("Email not verified. Enter OTP sent to your inbox."); return; } setError(String(data?.error || data?.message || "Invalid login credentials.")); return; } const accessToken = String(data?.access_token || "").trim(); if (typeof window !== "undefined" && accessToken) { window.sessionStorage.setItem("nxtgauge_access_token", accessToken); window.sessionStorage.setItem("nxtgauge_frontend_access_token", accessToken); } const resolvedActiveRole = resolveActiveRole( data?.user?.active_role || data?.user?.role, data?.user?.email || email().trim().toLowerCase() ); const normalizedEmail = email().trim().toLowerCase(); const userPayload = { id: String(data?.user?.id || ""), email: String(data?.user?.email || normalizedEmail), full_name: String(data?.user?.full_name || ""), active_role: resolvedActiveRole, email_verified: Boolean(data?.user?.email_verified ?? true), }; saveUser({ ...(data?.user || {}), ...userPayload }); if (auth.setUser) { auth.setUser(userPayload); } navigate("/dashboard", { replace: true }); } catch { setError("Network error during login. Please try again."); } finally { setSubmitting(false); } }; const resendOtp = async () => { if (submitting()) return; setError(""); setSubmitting(true); try { const res = await fetch("/api/auth/resend-otp", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, credentials: "include", body: JSON.stringify({ email: email().trim().toLowerCase() }), }); const data = await res.json().catch(() => ({})); if (!res.ok) { setError(String(data?.error || data?.message || "Unable to resend OTP.")); } } finally { setSubmitting(false); } }; const verifyThenLogin = async () => { if (submitting()) return; setError(""); if (otpCode().length !== 6) { setError("Enter a valid 6-digit OTP."); return; } setSubmitting(true); try { const verifyRes = await fetch("/api/gateway/api/auth/verify-email", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, credentials: "include", body: JSON.stringify({ otp: otpCode() }), }); const verifyData = await verifyRes.json().catch(() => ({})); if (!verifyRes.ok) { setError(String(verifyData?.error || verifyData?.message || "OTP verification failed.")); return; } await login(); } finally { setSubmitting(false); } }; return (
Public Workspace

Public Workspace

Welcome Back To Nxtgauge

Sign in to manage your profile, portfolio, and verification in one place.

Sign In

{ const value = e.currentTarget.value; setEmail(value); void lookupRoleByEmail(value); }} onBlur={(e) => { void lookupRoleByEmail(e.currentTarget.value); }} placeholder="Enter your email" />

{email().trim() && isValidEmail(email()) ? "✓ Valid email format" : "• Enter a valid email format"}

{checkingRole() ? "Checking account role..." : `• ${roleHint()}`}

setPassword(e.currentTarget.value)} placeholder="Enter your password" />
setCaptchaInput(e.currentTarget.value)} placeholder="Enter captcha" />
index)}> {(index) => ( setOtpDigit(index, e.currentTarget.value)} /> )}

{error()}

); }