import React, { useEffect, useState } from 'react'; import { LoginComponentProps } from './types'; import { getTranslations } from '../../shared/utils/localization-util'; import { newPassword } from './login-services'; import { jwtDecode } from 'jwt-decode'; interface TouchedFields { password?: boolean; repeatPassword?: boolean; } interface FormFields { password: string; repeatPassword: string; } const ResetPasswordComponent: React.FC = ({ tideClientConfig, languageCode, handleBackToLogin }) => { const [passwordSet, setPasswordSet] = useState(false); const [touched, setTouched] = useState({ password: false, repeatPassword: false }); const [formValues, setFormValues] = useState({ password: '', repeatPassword: '' }); const [submitted, setSubmitted] = useState(false); const [errors, setErrors] = useState({} as any); const [token, setToken] = useState(null); const [invalidToken, setInvalidToken] = useState(false); const translations = getTranslations(languageCode ?? 'en-GB'); useEffect(() => { if (typeof window !== 'undefined') { const rawToken = new URLSearchParams(window.location.search).get('token'); setToken(rawToken); if (rawToken) { try { const decoded = jwtDecode(rawToken); if (!decoded.exp || decoded.exp * 1000 < Date.now()) { setInvalidToken(true); } } catch (e) { setInvalidToken(true); } } else { setInvalidToken(true); } } }, []); const handleBlur = (event: React.ChangeEvent) => { setTouched((prev) => ({ ...prev, [event.target.name]: true })); validate(); }; const onChange = (event: React.ChangeEvent) => { setFormValues({ ...formValues, [event.target.name]: event.target.value }); validate(); }; const validate = () => { const validationErrors: any = {}; const { password, repeatPassword } = formValues; // Validate password if (!password) { validationErrors.password = 'required'; } else if (touched.password || submitted) { const hasNumber = /\d/.test(password); const hasCapital = /[A-Z]/.test(password); if (!hasNumber || !hasCapital) { validationErrors.passwordComplexity = 'invalid'; } } // Validate repeat password if (!repeatPassword) { validationErrors.repeatPassword = 'required'; } else if ((touched.repeatPassword || submitted) && password && repeatPassword !== password) { validationErrors.matchPassword = 'mismatch'; } setErrors(validationErrors); return Object.keys(validationErrors).length === 0; }; const handleResetPassword = async (e: React.MouseEvent) => { e.preventDefault(); setSubmitted(true); setTouched({ password: true, repeatPassword: true }); const isValid = validate(); if (!isValid) return; if (token) { try { const response = await newPassword(token, formValues.password, tideClientConfig); if (response) { setPasswordSet(true); } } catch { setErrors((prevErrors: any) => ({ ...prevErrors, api: true })); setInvalidToken(true); } } }; useEffect(() => { validate(); }, [formValues, token]); return ( <> {!passwordSet && !invalidToken && ( <>

{translations.LOGIN.RESET_PASSWORD_TITLE}

e.preventDefault()} noValidate>
{(touched.password || submitted) && (errors.password || errors.passwordComplexity) && (
{errors.password === 'required' && translations.LOGIN.PASSWORD_REQUIRED} {errors.passwordComplexity === 'invalid' && translations.LOGIN.PASSWORD_COMPLEXITY}
)}
{(touched.repeatPassword || submitted) && (errors.repeatPassword || errors.matchPassword) && (
{errors.repeatPassword === 'required' && translations.LOGIN.REPEAT_PASSWORD_REQUIRED} {errors.matchPassword === 'mismatch' && translations.LOGIN.PASSWORDS_DO_NOT_MATCH}
)}
)} {passwordSet && (

{translations.LOGIN.RESET_PASSWORD_TITLE}

{translations.LOGIN.RESET_PASSWORD_SUCCESSFUL_MESSAGE}

)} {invalidToken && (

{translations.LOGIN.RESET_PASSWORD_INVALID_TOKEN_TITLE}

{translations.LOGIN.RESET_PASSWORD_INVALID_TOKEN_DESCRIPTION}

)} ); }; export default ResetPasswordComponent;