All files / src/pages ResetPasswordPage.tsx

97.14% Statements 34/35
100% Branches 12/12
80% Functions 4/5
97.14% Lines 34/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121                1x 34x 34x     34x 34x 34x 34x 34x   34x 7x 7x 7x   7x 1x 1x       6x 1x 1x     5x 5x 5x   5x 5x 3x 2x 2x   2x       3x 3x 3x   5x       34x                                                 7x                   7x                                                        
// src/pages/ResetPasswordPage.tsx
import React, { useState } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import * as apiClient from '../services/apiClient';
import { logger } from '../services/logger.client';
import { LoadingSpinner } from '../components/LoadingSpinner';
import { PasswordInput } from '../components/PasswordInput';
 
export const ResetPasswordPage: React.FC = () => {
  const { token } = useParams<{ token: string }>();
  const navigate = useNavigate();
 
  // State for both password fields
  const [password, setPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [message, setMessage] = useState('');
 
  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');
    setMessage('');
 
    if (!token) {
      setError('No reset token provided. Please use the link from your email.');
      return;
    }
 
    // Add validation to check if passwords match
    if (password !== confirmPassword) {
      setError('Passwords do not match.');
      return;
    }
 
    setLoading(true);
    setError('');
    setMessage('');
 
    try {
      const response = await apiClient.resetPassword(token, password);
      const data = await response.json();
      setMessage(data.message + ' You will be redirected to the homepage shortly.');
      logger.info('Password has been successfully reset.');
      // After a short delay to allow the user to read the message, navigate them to the home page.
      setTimeout(() => {
        navigate('/');
      }, 4000); // 4-second delay
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : 'An unknown error occurred.';
      setError(errorMessage);
      logger.error({ err }, 'Failed to reset password.');
    } finally {
      setLoading(false);
    }
  };
 
  return (
    <div className="min-h-screen bg-gray-100 dark:bg-gray-950 flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
      <div className="max-w-md w-full space-y-8 bg-white dark:bg-gray-800 p-10 rounded-xl shadow-lg">
        <div>
          <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-white">
            Set a New Password
          </h2>
        </div>
        {message ? (
          <div className="text-center">
            <p className="text-green-600 dark:text-green-400">{message}</p>
            <Link
              to="/"
              className="mt-4 inline-block font-medium text-brand-primary hover:underline"
            >
              Return to Home
            </Link>
          </div>
        ) : (
          <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
            <div className="rounded-md shadow-sm space-y-4">
              <PasswordInput
                id="new-password"
                name="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                required
                placeholder="New Password" // This field already exists
                showStrength
              />
              {/* Add the missing confirm password field */}
              <PasswordInput
                id="confirm-new-password"
                name="confirmPassword"
                value={confirmPassword}
                onChange={(e) => setConfirmPassword(e.target.value)}
                required
                placeholder="Confirm New Password"
              />
            </div>
            {error && <p className="text-sm text-red-600 dark:text-red-400 text-center">{error}</p>}
            <div>
              <button
                type="submit"
                disabled={loading}
                aria-label="Reset Password"
                className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-brand-secondary hover:bg-brand-dark focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-brand-primary disabled:bg-gray-400"
              >
                {loading ? (
                  <div className="w-5 h-5">
                    <LoadingSpinner />
                  </div>
                ) : (
                  'Reset Password'
                )}
              </button>
            </div>
          </form>
        )}
      </div>
    </div>
  );
};