import { createSignal, createEffect, Show } from "solid-js"; import { A, useNavigate, useSearchParams } from "@solidjs/router"; import { PageHead } from "~/components/PageHead"; import CountdownCircleTimer from "~/components/CountdownCircleTimer"; import { validatePassword } from "~/lib/validation"; import { api } from "~/lib/api"; import { VALIDATION_CONFIG, COUNTDOWN_CONFIG } from "~/config"; import PasswordInput from "~/components/ui/PasswordInput"; import PasswordStrengthMeter from "~/components/PasswordStrengthMeter"; import { Button } from "~/components/ui/Button"; export default function PasswordResetPage() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const [passwordBlurred, setPasswordBlurred] = createSignal(false); const [passwordChangeLoading, setPasswordChangeLoading] = createSignal(false); const [passwordsMatch, setPasswordsMatch] = createSignal(false); const [showPasswordLengthWarning, setShowPasswordLengthWarning] = createSignal(false); const [passwordLengthSufficient, setPasswordLengthSufficient] = createSignal(false); const [showRequestNewEmail, setShowRequestNewEmail] = createSignal(false); const [countDown, setCountDown] = createSignal(false); const [error, setError] = createSignal(""); const [newPassword, setNewPassword] = createSignal(""); let newPasswordRef: HTMLInputElement | undefined; let newPasswordConfRef: HTMLInputElement | undefined; const token = searchParams.token; createEffect(() => { if (!token) { navigate("/login/request-password-reset"); } }); const setNewPasswordTrigger = async (e: Event) => { e.preventDefault(); setShowRequestNewEmail(false); setError(""); if (!newPasswordRef || !newPasswordConfRef) { setError("Please fill in all fields"); return; } const newPassword = newPasswordRef.value; const newPasswordConf = newPasswordConfRef.value; const passwordValidation = validatePassword(newPassword); if (!passwordValidation.isValid) { setError(passwordValidation.errors[0] || "Invalid password"); return; } if (newPassword !== newPasswordConf) { setError("Passwords do not match"); return; } setPasswordChangeLoading(true); try { const result = await api.auth.resetPassword.mutate({ token: token, newPassword, newPasswordConfirmation: newPasswordConf }); if (result.success) { setCountDown(true); } else { setError("Failed to reset password"); } } catch (err: any) { console.error("Password reset error:", err); const errorMsg = err.message || "An error occurred. Please try again."; if (errorMsg.includes("expired") || errorMsg.includes("token")) { setShowRequestNewEmail(true); setError("Token has expired"); } else { setError(errorMsg); } } finally { setPasswordChangeLoading(false); } }; const checkForMatch = (newPassword: string, newPasswordConf: string) => { if (newPassword === newPasswordConf) { setPasswordsMatch(true); } else { setPasswordsMatch(false); } }; const checkPasswordLength = (password: string) => { if (password.length >= VALIDATION_CONFIG.MIN_PASSWORD_LENGTH) { setPasswordLengthSufficient(true); setShowPasswordLengthWarning(false); } else { setPasswordLengthSufficient(false); if (passwordBlurred()) { setShowPasswordLengthWarning(true); } } }; const passwordLengthBlurCheck = () => { if ( !passwordLengthSufficient() && newPasswordRef && newPasswordRef.value !== "" ) { setShowPasswordLengthWarning(true); } setPasswordBlurred(true); }; const handleNewPasswordChange = (e: Event) => { const target = e.target as HTMLInputElement; setNewPassword(target.value); checkPasswordLength(target.value); if (newPasswordConfRef) { checkForMatch(target.value, newPasswordConfRef.value); } }; const handlePasswordConfChange = (e: Event) => { const target = e.target as HTMLInputElement; if (newPasswordRef) { checkForMatch(newPasswordRef.value, target.value); } }; const handlePasswordBlur = () => { passwordLengthBlurCheck(); }; const renderTime = (timeRemaining: number) => { if (timeRemaining === 0) { navigate("/login"); } return (
Change Successful!
{timeRemaining}
Redirecting...
); }; return ( <>
Set New Password
setNewPasswordTrigger(e)} class="mt-4 flex w-full justify-center" >
Password too short! Min Length:{" "} {VALIDATION_CONFIG.MIN_PASSWORD_LENGTH}
= VALIDATION_CONFIG.MIN_PASSWORD_CONF_LENGTH_FOR_ERROR ? "" : "opacity-0 select-none" } text-red text-center transition-opacity duration-200 ease-in-out`} > Passwords do not match!
Set New Password } >
false} > {({ remainingTime }) => renderTime(remainingTime)}
{error()}
Token has expired, request a new one{" "} here
); }