import { useNavigate } from '@solidjs/router'; import { createMemo, createSignal, onMount } from 'solid-js'; import { hasAdminSession, setAdminSession } from '~/lib/admin-session'; type AuthMode = 'login' | 'reset'; type ResetStep = 'request' | 'verify'; function pickChallengeId(payload: any): string { const direct = String(payload?.challengeId || '').trim(); if (direct) return direct; const nested = String(payload?.data?.challengeId || '').trim(); if (nested) return nested; const snake = String(payload?.challenge_id || '').trim(); if (snake) return snake; return ''; } function pickMaskedEmail(payload: any, fallback: string): string { const direct = String(payload?.maskedEmail || '').trim(); if (direct) return direct; const nested = String(payload?.data?.maskedEmail || '').trim(); if (nested) return nested; return fallback; } export default function LoginPage() { const navigate = useNavigate(); const [mode, setMode] = createSignal('login'); const [resetStep, setResetStep] = createSignal('request'); const [email, setEmail] = createSignal(''); const [password, setPassword] = createSignal(''); const [resetCode, setResetCode] = createSignal(''); const [newPassword, setNewPassword] = createSignal(''); const [confirmPassword, setConfirmPassword] = createSignal(''); const [challengeId, setChallengeId] = createSignal(''); const [maskedEmail, setMaskedEmail] = createSignal(''); const [isSubmitting, setIsSubmitting] = createSignal(false); const [error, setError] = createSignal(''); const [info, setInfo] = createSignal(''); const canSubmitResetRequest = createMemo( () => email().trim().length > 0 && newPassword().trim().length > 0 && confirmPassword().trim().length > 0, ); const canSubmitResetVerify = createMemo( () => challengeId().trim().length > 0 && resetCode().trim().length === 6, ); const canSubmitLoginCredentials = createMemo( () => email().trim().length > 0 && password().trim().length > 0, ); const clearMessages = () => { setError(''); setInfo(''); }; const resetPasswordFlow = () => { setResetStep('request'); setResetCode(''); setChallengeId(''); setMaskedEmail(''); setNewPassword(''); setConfirmPassword(''); }; const switchMode = (nextMode: AuthMode) => { clearMessages(); setMode(nextMode); if (nextMode === 'login') { resetPasswordFlow(); } }; onMount(() => { if (hasAdminSession()) { navigate('/admin', { replace: true }); } }); const completeAdminLogin = () => { setAdminSession(); const params = new URLSearchParams(window.location.search); const from = params.get('from'); const nextPath = from && from.startsWith('/admin') ? from : '/admin'; navigate(nextPath, { replace: true }); }; const directSignIn = async () => { clearMessages(); if (!canSubmitLoginCredentials()) { setError('Email and password are required.'); return; } setIsSubmitting(true); try { completeAdminLogin(); } catch (nextError: any) { setError(String(nextError?.message || 'Sign in failed.')); } finally { setIsSubmitting(false); } }; const requestResetCode = async () => { clearMessages(); if (!canSubmitResetRequest()) { setError('Email, new password, and confirm password are required.'); return; } if (newPassword() !== confirmPassword()) { setError('Passwords do not match.'); return; } const trimmedEmail = email().trim().toLowerCase(); const resolvedPassword = newPassword().trim(); const requestPayload = { email: trimmedEmail, newPassword: resolvedPassword, new_password: resolvedPassword, password: resolvedPassword, }; const attempts: Array<{ url: string; body: string }> = [ { url: '/api/gateway/users/auth/internal/forgot-password/request-code', body: JSON.stringify(requestPayload), }, { url: '/api/gateway/auth/internal/forgot-password/request-code', body: JSON.stringify(requestPayload), }, { url: '/api/gateway/users/auth/internal/forgot-password/request-code', body: JSON.stringify({ data: requestPayload }), }, { url: '/api/gateway/auth/internal/forgot-password/request-code', body: JSON.stringify({ data: requestPayload }), }, ]; setIsSubmitting(true); try { let payload: any = {}; let status = 500; for (const attempt of attempts) { const response = await fetch(attempt.url, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', }, body: attempt.body, }); status = response.status; payload = await response.json().catch(() => ({})); if (response.ok) break; } const nextChallengeId = pickChallengeId(payload); if (!nextChallengeId) { const fallbackMessage = status === 502 ? 'Verification service is temporarily unavailable (502). Please retry in 1-2 minutes.' : 'Failed to send reset code.'; throw new Error(String(payload?.message || payload?.error || fallbackMessage).trim()); } setChallengeId(nextChallengeId); setMaskedEmail(pickMaskedEmail(payload, trimmedEmail)); setResetStep('verify'); const debugCode = String(payload?.debugCode || payload?.data?.debugCode || '').trim(); setInfo(debugCode ? `Reset code sent. [DEV CODE: ${debugCode}]` : 'Reset code sent to your email.'); } catch (nextError: any) { setError(String(nextError?.message || 'Failed to send reset code.')); } finally { setIsSubmitting(false); } }; const verifyResetCode = async () => { clearMessages(); if (!canSubmitResetVerify()) { setError('A valid 6-digit verification code is required.'); return; } if (newPassword().trim() !== confirmPassword().trim()) { setError('Passwords do not match.'); return; } const resolvedPassword = newPassword().trim(); const resolvedChallengeId = challengeId().trim(); const resolvedCode = resetCode().trim(); const verifyPayload = { challengeId: resolvedChallengeId, challenge_id: resolvedChallengeId, code: resolvedCode, otp: resolvedCode, verificationCode: resolvedCode, verification_code: resolvedCode, newPassword: resolvedPassword, new_password: resolvedPassword, password: resolvedPassword, }; const attempts: string[] = [ '/api/gateway/users/auth/internal/forgot-password/verify-code', '/api/gateway/auth/internal/forgot-password/verify-code', ]; setIsSubmitting(true); try { let payload: any = {}; let status = 500; let success = false; for (const url of attempts) { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', }, body: JSON.stringify(verifyPayload), }); status = response.status; payload = await response.json().catch(() => ({})); if (response.ok) { success = true; break; } } if (!success) { const fallbackMessage = status === 502 ? 'Verification service is temporarily unavailable (502). Please retry in 1-2 minutes.' : 'Password reset failed.'; throw new Error(String(payload?.message || payload?.error || fallbackMessage).trim()); } setPassword(''); switchMode('login'); setInfo('Password reset successful. Please sign in with your new password.'); } catch (nextError: any) { setError(String(nextError?.message || 'Password reset failed.')); } finally { setIsSubmitting(false); } }; return (

Internal Access

Welcome back to Nxtgauge.

Sign in securely to access the admin control panel.

Office workspace

{mode() === 'login' ? 'Employee Login' : 'Reset Password'}

{ setEmail(event.currentTarget.value); clearMessages(); }} placeholder="Enter your email" />
{mode() === 'login' ? ( <>
{ setPassword(event.currentTarget.value); clearMessages(); }} placeholder="Enter your password" />
) : ( <>
{ setNewPassword(event.currentTarget.value); clearMessages(); }} placeholder="Enter your new password" />
{ setConfirmPassword(event.currentTarget.value); clearMessages(); }} placeholder="Confirm your new password" />
{resetStep() === 'verify' ? (
{ setResetCode(event.currentTarget.value.replace(/\D/g, '').slice(0, 6)); clearMessages(); }} placeholder="Enter 6-digit code" />

Code sent to {maskedEmail() || email()}.

) : null}
{resetStep() === 'request' ? ( ) : ( )}
)}
{info() ?

{info()}

: null} {error() ?

{error()}

: null}
); }