Skip to content

[f] 080 - Reset and Forget password page#24

Merged
giahung68 merged 5 commits intomainfrom
features/080-forget-password-page
Oct 28, 2024
Merged

[f] 080 - Reset and Forget password page#24
giahung68 merged 5 commits intomainfrom
features/080-forget-password-page

Conversation

@giahung68
Copy link
Collaborator

@giahung68 giahung68 commented Oct 28, 2024

Story

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced "Forgot Password" and "Reset Password" functionalities with dedicated routes.
    • Added user-friendly components for password recovery and reset processes.
    • Enhanced the header with a dropdown menu for user actions, including logout.
  • Bug Fixes

    • Improved navigation by linking the "Forgot password?" button to the new password recovery route.
  • Documentation

    • Updated routing constants to include new paths for password-related actions.

These changes enhance user experience by providing streamlined access to password recovery options and improving navigation throughout the application.

@coderabbitai
Copy link

coderabbitai bot commented Oct 28, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The changes in this pull request introduce new components and routing capabilities to the application, specifically for password management. The ForgetPassword and ResetPassword components are added, along with their corresponding routes. Modifications to the HeaderBar enhance user interaction with a dropdown menu for logging out. The LoginPage now includes navigation to the password recovery route. Other components like OnboardingPage receive minor layout adjustments. Overall, the updates focus on improving user authentication processes without altering existing logic significantly.

Changes

File Change Summary
src/App.tsx Added routes for ForgetPassword and ResetPassword components; updated imports and constants.
src/components/ForgetPassword.tsx Implemented a new component for password reset requests; manages form state and error handling.
src/components/HeaderBar.tsx Removed HeaderBarProps interface; added dropdown menu for logout functionality.
src/components/LoginPage.tsx Added onClick handler for "Forgot password?" button to navigate to the password recovery route.
src/components/OnboardingPage.tsx Integrated PublicHeader component into the layout.
src/components/ResetPassword.tsx Implemented a new component for resetting passwords; includes form validation and session handling.
src/components/SearchInterface.tsx Removed Logout button from the component.
src/constants/routes.ts Added constants for FORGET_PASSWORD_PATH and RESET_PASSWORD_PATH; updated export statement.

Possibly related PRs

Suggested reviewers

  • Dat-H-Tran

Poem

🐇 In the land of code where rabbits play,
New paths for passwords brighten the way.
With forms and buttons, we hop with glee,
Forget and reset, so simple and free!
A dropdown for logout, a header so bright,
In our cozy app, everything feels right! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Outside diff range and nitpick comments (5)
src/constants/routes.ts (2)

4-4: Consider grouping related route constants together.

For better organization and maintainability, consider grouping the authentication-related routes (LOGIN_PATH, LOGOUT_PATH, FORGET_PASSWORD_PATH, RESET_PASSWORD_PATH) together in the file.

const ONBOARDING_PATH = '/onboarding'
const LOGIN_PATH = '/login'
const LOGOUT_PATH = '/logout'
-const FORGET_PASSWORD_PATH = '/forget-password'
const ROOT_PATH = '/'
const BROADCAST_PATH = `${import.meta.env.VITE_BASE_URL}/broadcasts`
-const RESET_PASSWORD_PATH = '/reset-password'
+const FORGET_PASSWORD_PATH = '/forget-password'
+const RESET_PASSWORD_PATH = '/reset-password'

Also applies to: 7-7


4-4: Consider using 'forgot-password' instead of 'forget-password'.

The term "forgot password" is more commonly used in user interfaces and follows established UX patterns. This change would align better with user expectations.

-const FORGET_PASSWORD_PATH = '/forget-password'
+const FORGOT_PASSWORD_PATH = '/forgot-password'
src/components/HeaderBar.tsx (1)

39-62: Enhance dropdown menu accessibility

While the implementation is solid, it could benefit from improved accessibility attributes.

Consider these accessibility enhancements:

  <DropdownMenu>
-   <DropdownMenuTrigger asChild>
+   <DropdownMenuTrigger asChild aria-label="User menu">
      <Button variant='ghost' size='icon' className='h-8 w-8 p-0 hover:bg-gray-50'>
        {user?.user_metadata.avatar_url ? (
          <img
            src={user.user_metadata.avatar_url}
            alt='User Avatar'
            className='h-6 w-6 rounded-full'
            referrerPolicy='no-referrer'
          />
        ) : (
          <div className='flex h-6 w-6 items-center justify-center rounded-full bg-blue-600 text-xs text-white'
+            role="img"
+            aria-label={`User initials: ${getInitials(user?.email || '')}`}
          >
            {getInitials(user?.email || '')}
          </div>
        )}
      </Button>
    </DropdownMenuTrigger>
-   <DropdownMenuContent align='end' className='w-48'>
+   <DropdownMenuContent align='end' className='w-48' aria-label="User menu options">
src/components/ForgetPassword.tsx (1)

56-64: Enhance form accessibility.

The form could benefit from improved accessibility attributes and error handling.

 <form onSubmit={handleSubmit(onSubmit)} className='space-y-4'>
   <Input
     id='email'
     type='email'
+    aria-label="Email address"
+    aria-describedby="email-error"
     placeholder='Your Email'
     className='w-full rounded-md border border-gray-300 px-4 py-2 focus:border-blue-500 focus:outline-none'
     {...register('email', { required: 'Email is required' })}
   />
-  {errors.email && <p className='mt-1 text-xs text-red-500'>{errors.email.message}</p>}
+  {errors.email && <p id="email-error" role="alert" className='mt-1 text-xs text-red-500'>{errors.email.message}</p>}
src/components/LoginPage.tsx (1)

101-103: LGTM! Consider enhancing accessibility.

The "Forgot password?" navigation is implemented correctly and well-integrated with the existing login form.

Consider these accessibility improvements:

-              <Button variant='link' className='h-auto p-0 text-blue-600' onClick={() => navigate('/forget-password')}>
+              <Button 
+                variant='link' 
+                className='h-auto p-0 text-blue-600' 
+                onClick={() => navigate('/forget-password')}
+                aria-label="Navigate to forgot password page"
+              >
                 Forgot password?
               </Button>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 03dd99e and bb06477.

📒 Files selected for processing (8)
  • src/App.tsx (2 hunks)
  • src/components/ForgetPassword.tsx (1 hunks)
  • src/components/HeaderBar.tsx (2 hunks)
  • src/components/LoginPage.tsx (1 hunks)
  • src/components/OnboardingPage.tsx (2 hunks)
  • src/components/ResetPassword.tsx (1 hunks)
  • src/components/SearchInterface.tsx (0 hunks)
  • src/constants/routes.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/components/SearchInterface.tsx
🧰 Additional context used
🪛 Biome
src/components/ForgetPassword.tsx

[error] 100-100: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

🔇 Additional comments (10)
src/constants/routes.ts (1)

9-17: LGTM! Well-organized exports.

The export statement is well-structured with:

  • One constant per line for better git diff tracking
  • Alphabetical ordering for easy scanning
src/App.tsx (2)

Line range hint 1-46: Overall structure maintains good separation of concerns.

The changes integrate well with the existing architecture, maintaining a clear separation between public and authenticated routes. The file remains clean and organized.


28-29: Routes are correctly implemented and positioned.

The new routes are appropriately placed with other public routes and follow the established pattern. However, since these are password recovery endpoints, we should ensure they're protected against abuse.

Let's verify if rate limiting is implemented for these endpoints:

src/components/HeaderBar.tsx (1)

3-3: LGTM: Import statements are properly structured

The new imports for the LogOut icon and dropdown menu components are correctly organized and sourced from appropriate locations.

Also applies to: 10-10

src/components/ForgetPassword.tsx (2)

1-11: LGTM! Imports and type definitions are well-structured.

The imports are appropriate and the FormData type is well-defined for the form handling requirements.


93-96: Good implementation of email masking for privacy.

The email masking implementation helps protect user privacy while still providing enough information for recognition.

src/components/ResetPassword.tsx (2)

1-15: LGTM! Clean imports and type definitions.

The imports are well-organized and the FormData type properly defines the expected form structure.


1-176: Verify route configuration and navigation.

Let's verify that this component is properly integrated into the application's routing structure.

✅ Verification successful

Route configuration and navigation are properly implemented

The verification confirms that:

  • The ResetPassword component is correctly registered in App.tsx with the proper route path
  • The route constant RESET_PASSWORD_PATH is properly defined and used
  • The component is accessible through a public route (not behind authentication)
  • The reset password flow is properly integrated with ForgetPassword component, which includes the correct redirectTo URL
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify route configuration and navigation setup

# Check route constant definition
echo "Checking for route constant..."
rg "RESET_PASSWORD_PATH.*=.*['\"].*['\"]" -g "*.{ts,tsx}"

# Check route component registration
echo "Checking route registration..."
rg "Route.*path.*=.*(?i)reset.*password.*component.*=.*ResetPassword" -g "*.{ts,tsx}"

# Check navigation to reset password
echo "Checking navigation to reset password..."
rg "navigate\(.*(?i)reset.*password.*\)" -g "*.{ts,tsx}"

Length of output: 513


Script:

#!/bin/bash
# Let's verify the route registration in the router configuration

# First check for router setup files
echo "Checking router configuration files..."
fd "router|routes" -e ts -e tsx

# Then check for ResetPassword component registration in router
echo "Checking ResetPassword route registration..."
rg -A 2 -B 2 "ResetPassword" -g "*router*.{ts,tsx}"

# Check for imports of ResetPassword component
echo "Checking ResetPassword imports..."
rg "import.*ResetPassword" -g "*.{ts,tsx}"

# Check for any navigation to /reset-password path
echo "Checking navigation to reset-password path..."
rg "('/reset-password'|/reset-password/)" -g "*.{ts,tsx}"

Length of output: 874


Script:

#!/bin/bash
# Let's check the App.tsx for route registration since we found the import there

echo "Checking App.tsx route configuration..."
rg -A 5 -B 5 "<Route.*ResetPassword" src/App.tsx

# Also check for any auth-related route protection
echo "Checking for auth protection patterns..."
rg -A 3 -B 3 "ProtectedRoute|AuthRoute|RequireAuth" src/App.tsx

# Check for any email-related navigation to reset password
echo "Checking email-related reset password navigation..."
rg "resetPassword|reset.*password" -g "*.{ts,tsx}" -i

Length of output: 2485

src/components/OnboardingPage.tsx (2)

11-11: LGTM: Clean import addition

The PublicHeader import follows the existing import organization pattern.


153-153: Verify PublicHeader integration and layout

The PublicHeader component is appropriately placed, but please verify:

  1. Visual consistency with other auth pages (login, forget password, reset password)
  2. Layout spacing and mobile responsiveness with the new header

Consider documenting any required props or configuration for PublicHeader in a comment, even if none are currently needed, to help future maintainers understand the component's interface.

✅ Verification successful

PublicHeader integration is consistent across auth pages

The verification shows that PublicHeader is consistently implemented across all authentication-related pages:

  • ResetPassword.tsx
  • OnboardingPage.tsx
  • ForgetPassword.tsx

All pages follow similar patterns with centered content layouts and appropriate spacing through flex and padding classes. The implementation in OnboardingPage.tsx aligns with the established pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of PublicHeader to ensure consistent implementation
ast-grep --pattern 'import PublicHeader from "./PublicHeader"'
rg -A 2 '<PublicHeader' 

Length of output: 825

import PrivateRoute from './components/PrivateRoute'
import OnboardingPage from './components/OnboardingPage'
import AuthenticatedLayout from './layouts/AuthenticatedLayout'
import ForgetPassword from './components/ForgetPassword'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Maintain consistent import style across components.

The import styles are inconsistent between ForgetPassword (default import) and ResetPassword (named import). This could indicate inconsistent export patterns in these components.

Consider using the same import style for both components. If they're similar components, they should follow the same pattern:

-import ForgetPassword from './components/ForgetPassword'
-import { ResetPassword } from './components/ResetPassword'
+import { ForgetPassword } from './components/ForgetPassword'
+import { ResetPassword } from './components/ResetPassword'

Or:

-import ForgetPassword from './components/ForgetPassword'
-import { ResetPassword } from './components/ResetPassword'
+import ForgetPassword from './components/ForgetPassword'
+import ResetPassword from './components/ResetPassword'

Also applies to: 16-16

}

const HeaderBar: React.FC<HeaderBarProps> = () => {
const HeaderBar: React.FC = () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Type mismatch in HeaderBar component usage

The verification revealed an issue: AuthenticatedLayout is passing a user prop to HeaderBar (<HeaderBar user={user} />), but HeaderBar is defined without props (React.FC = ()). The component already accesses user data through the useAuth hook internally.

  • Remove the user prop from HeaderBar usage in src/layouts/AuthenticatedLayout.tsx
  • The authentication integration itself is properly implemented using Supabase auth and the useAuth hook
🔗 Analysis chain

Verify authentication integration

Since HeaderBarProps has been removed, we should verify its impact on the codebase.


Let me check the actual implementation and usage of HeaderBar to verify the authentication integration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to HeaderBarProps
rg "HeaderBarProps" 

# Check for other components using the HeaderBar component
rg -l "HeaderBar"

Length of output: 102


Script:

#!/bin/bash
# Check HeaderBar implementation
cat src/components/HeaderBar.tsx

# Check how HeaderBar is used in AuthenticatedLayout
cat src/layouts/AuthenticatedLayout.tsx

Length of output: 5994

Comment on lines +23 to +25
const handleLogout = async () => {
await supabase.auth.signOut()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling to logout function

The current implementation lacks error handling and loading state management. This could lead to a poor user experience if the signOut operation fails.

Consider implementing error handling and loading state:

+ const [isLoggingOut, setIsLoggingOut] = useState(false)
  const handleLogout = async () => {
+   try {
+     setIsLoggingOut(true)
      await supabase.auth.signOut()
+   } catch (error) {
+     console.error('Error signing out:', error)
+     // Consider adding a toast notification here
+   } finally {
+     setIsLoggingOut(false)
+   }
  }

Then update the dropdown menu item:

- <DropdownMenuItem className='cursor-pointer' onClick={handleLogout}>
+ <DropdownMenuItem 
+   className='cursor-pointer' 
+   onClick={handleLogout}
+   disabled={isLoggingOut}
+ >

Committable suggestion was skipped due to low confidence.

Comment on lines +13 to +22
export default function ForgetPassword() {
const [isLoading, setIsLoading] = useState(false)
const [isSuccess, setIsSuccess] = useState(false)
const [email, setEmail] = useState('')

const {
register,
handleSubmit,
formState: { errors }
} = useForm<FormData>()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider optimizing state management.

The email state variable might be redundant since the email value is already managed by react-hook-form. You could access it from the form data when needed.

export default function ForgetPassword() {
  const [isLoading, setIsLoading] = useState(false)
  const [isSuccess, setIsSuccess] = useState(false)
-  const [email, setEmail] = useState('')

  const {
    register,
    handleSubmit,
+   watch,
    formState: { errors }
  } = useForm<FormData>()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default function ForgetPassword() {
const [isLoading, setIsLoading] = useState(false)
const [isSuccess, setIsSuccess] = useState(false)
const [email, setEmail] = useState('')
const {
register,
handleSubmit,
formState: { errors }
} = useForm<FormData>()
export default function ForgetPassword() {
const [isLoading, setIsLoading] = useState(false)
const [isSuccess, setIsSuccess] = useState(false)
const {
register,
handleSubmit,
watch,
formState: { errors }
} = useForm<FormData>()

Comment on lines +99 to +103
<Button
onClick={() => (window.location.href = '/login')}
className='w-full rounded-md bg-blue-600 py-2 text-white hover:bg-blue-700 focus:outline-none'>
Return to Login
</Button>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Improve navigation handling.

Using direct window.location manipulation is not ideal in a React application. Consider using React Router's navigation hooks.

+ import { useNavigate } from 'react-router-dom';

export default function ForgetPassword() {
+  const navigate = useNavigate();
   // ... other code ...
   
   <Button
-    onClick={() => (window.location.href = '/login')}
+    onClick={() => navigate('/login')}
     className='w-full rounded-md bg-blue-600 py-2 text-white hover:bg-blue-700 focus:outline-none'>
     Return to Login
   </Button>

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 Biome

[error] 100-100: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

Comment on lines +24 to +41
const onSubmit: SubmitHandler<FormData> = async data => {
setIsLoading(true)
setEmail(data.email)

try {
const { error } = await supabase.auth.resetPasswordForEmail(data.email, {
redirectTo: `${window.location.origin}/reset-password`
})

if (error) throw error

setIsSuccess(true)
} catch (error) {
// Handle error case
} finally {
setIsLoading(false)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance error handling and user feedback.

The error handling is incomplete and doesn't provide feedback to users when something goes wrong.

    try {
      const { error } = await supabase.auth.resetPasswordForEmail(data.email, {
        redirectTo: `${window.location.origin}/reset-password`
      })

      if (error) throw error

      setIsSuccess(true)
    } catch (error) {
-     // Handle error case
+     console.error('Password reset error:', error)
+     toast({
+       title: "Error",
+       description: "Failed to send reset email. Please try again.",
+       variant: "destructive"
+     })
    } finally {
      setIsLoading(false)
    }

Committable suggestion was skipped due to low confidence.

Comment on lines +53 to +77
const onSubmit: SubmitHandler<FormData> = async data => {
setError('')
setIsLoading(true)

try {
const { error } = await supabase.auth.updateUser({
password: data.password
})

if (error) {
throw error
}
// Show success message or redirect
navigate('/login', {
state: {
message: 'Password successfully reset. Please login with your new password.'
}
})
} catch (err) {
console.error('Error:', err)
setError(err instanceof Error ? err.message : 'An error occurred while resetting the password.')
} finally {
setIsLoading(false)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance password validation and error handling.

The form submission logic needs improvements in error handling and password security:

  1. Missing password strength validation
  2. Production console.error
  3. Hardcoded success message

Consider these improvements:

+ const PASSWORD_REQUIREMENTS = {
+   minLength: 8,
+   requireNumbers: true,
+   requireSpecialChars: true,
+   requireUppercase: true
+ }

+ const SUCCESS_MESSAGE = 'Password successfully reset. Please login with your new password.'

 const onSubmit: SubmitHandler<FormData> = async data => {
   setError('')
   setIsLoading(true)

   try {
+    // Validate password strength
+    if (!/\d/.test(data.password)) {
+      throw new Error('Password must contain at least one number')
+    }
+    if (!/[!@#$%^&*]/.test(data.password)) {
+      throw new Error('Password must contain at least one special character')
+    }
+    if (!/[A-Z]/.test(data.password)) {
+      throw new Error('Password must contain at least one uppercase letter')
+    }

     const { error } = await supabase.auth.updateUser({
       password: data.password
     })

     if (error) {
       throw error
     }
     navigate('/login', {
       state: {
-        message: 'Password successfully reset. Please login with your new password.'
+        message: SUCCESS_MESSAGE
       }
     })
   } catch (err) {
-    console.error('Error:', err)
     setError(err instanceof Error ? err.message : 'An error occurred while resetting the password.')
   } finally {
     setIsLoading(false)
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const onSubmit: SubmitHandler<FormData> = async data => {
setError('')
setIsLoading(true)
try {
const { error } = await supabase.auth.updateUser({
password: data.password
})
if (error) {
throw error
}
// Show success message or redirect
navigate('/login', {
state: {
message: 'Password successfully reset. Please login with your new password.'
}
})
} catch (err) {
console.error('Error:', err)
setError(err instanceof Error ? err.message : 'An error occurred while resetting the password.')
} finally {
setIsLoading(false)
}
}
const PASSWORD_REQUIREMENTS = {
minLength: 8,
requireNumbers: true,
requireSpecialChars: true,
requireUppercase: true
}
const SUCCESS_MESSAGE = 'Password successfully reset. Please login with your new password.'
const onSubmit: SubmitHandler<FormData> = async data => {
setError('')
setIsLoading(true)
try {
// Validate password strength
if (!/\d/.test(data.password)) {
throw new Error('Password must contain at least one number')
}
if (!/[!@#$%^&*]/.test(data.password)) {
throw new Error('Password must contain at least one special character')
}
if (!/[A-Z]/.test(data.password)) {
throw new Error('Password must contain at least one uppercase letter')
}
const { error } = await supabase.auth.updateUser({
password: data.password
})
if (error) {
throw error
}
navigate('/login', {
state: {
message: SUCCESS_MESSAGE
}
})
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred while resetting the password.')
} finally {
setIsLoading(false)
}
}

Comment on lines +82 to +176
return (
<div className='min-h-screen'>
<PublicHeader />
<div className='mx-auto px-4 py-16'>
<Card className='mx-auto max-w-lg rounded-xl p-8'>
<CardHeader className='space-y-3 text-center'>
<CardTitle className='text-3xl font-bold tracking-tight text-gray-900'>Reset your Password</CardTitle>
<p className='text-gray-500'>Almost done. Enter your new password below.</p>
</CardHeader>

<CardContent className='mt-8'>
<form onSubmit={handleSubmit(onSubmit)} className='space-y-6'>
<div className='space-y-6'>
<div className='relative'>
<Label className='block text-sm font-medium text-gray-700' htmlFor='password'>
New Password
</Label>
<div className='relative mt-2'>
<Input
id='password'
type={showPassword ? 'text' : 'password'}
{...register('password', {
required: 'Password is required',
minLength: {
value: 6,
message: 'Password must be at least 6 characters long'
}
})}
className='block w-full rounded-lg border-gray-300 px-4 py-3 pr-10 focus:border-blue-500 focus:ring-blue-500'
placeholder='Enter new password'
/>
<button
type='button'
className='absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600'
onClick={() => setShowPassword(!showPassword)}>
{showPassword ? <EyeOff className='h-5 w-5' /> : <Eye className='h-5 w-5' />}
</button>
</div>
{errors.password && <p className='mt-2 text-sm text-red-600'>{errors.password.message}</p>}
</div>

<div className='relative'>
<Label className='block text-sm font-medium text-gray-700' htmlFor='confirmPassword'>
Confirm Password
</Label>
<div className='relative mt-2'>
<Input
id='confirmPassword'
type={showConfirmPassword ? 'text' : 'password'}
{...register('confirmPassword', {
required: 'Please confirm your password',
validate: value => value === password || 'The passwords do not match'
})}
className='block w-full rounded-lg border-gray-300 px-4 py-3 pr-10 focus:border-blue-500 focus:ring-blue-500'
placeholder='Confirm new password'
/>
<button
type='button'
className='absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600'
onClick={() => setShowConfirmPassword(!showConfirmPassword)}>
{showConfirmPassword ? <EyeOff className='h-5 w-5' /> : <Eye className='h-5 w-5' />}
</button>
</div>
{errors.confirmPassword && (
<p className='mt-2 text-sm text-red-600'>{errors.confirmPassword.message}</p>
)}
</div>
</div>

{error && (
<div className='rounded-md bg-red-50 p-4'>
<p className='text-sm text-red-600'>{error}</p>
</div>
)}

<Button
type='submit'
className='w-full rounded-lg bg-blue-600 px-4 py-3 text-white hover:bg-blue-700 focus:outline-none focus:ring-4 focus:ring-blue-300'
disabled={isLoading}>
{isLoading ? (
<div className='flex items-center justify-center'>
<Loader2 className='mr-2 h-5 w-5 animate-spin' />
<span>Please wait...</span>
</div>
) : (
'Reset Password'
)}
</Button>
</form>
</CardContent>
</Card>
</div>
</div>
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance accessibility and user experience.

The UI implementation needs accessibility improvements and could benefit from additional user feedback:

  1. Missing aria-labels for password visibility toggles
  2. No password strength indicator
  3. Missing aria-live region for error messages

Consider these improvements:

 <button
   type='button'
   className='absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600'
+  aria-label={showPassword ? 'Hide password' : 'Show password'}
   onClick={() => setShowPassword(!showPassword)}>
   {showPassword ? <EyeOff className='h-5 w-5' /> : <Eye className='h-5 w-5' />}
 </button>

+ {/* Add password strength indicator */}
+ <div className="mt-2">
+   <div className="h-1 w-full bg-gray-200 rounded">
+     <div
+       className={`h-1 rounded transition-all ${
+         password.length > 8 ? 'bg-green-500 w-full' : 'bg-red-500 w-1/3'
+       }`}
+     />
+   </div>
+ </div>

 {error && (
-  <div className='rounded-md bg-red-50 p-4'>
+  <div className='rounded-md bg-red-50 p-4' role="alert" aria-live="polite">
     <p className='text-sm text-red-600'>{error}</p>
   </div>
 )}

Committable suggestion was skipped due to low confidence.

Comment on lines +32 to +49
useEffect(() => {
const updateSession = async () => {
try {
if (params.access_token && params.refresh_token) {
const response = await supabase.auth.setSession({
access_token: params.access_token,
refresh_token: params.refresh_token
})
}
} catch (error) {
console.log(error)
setError('Invalid or expired reset link')
setTimeout(() => navigate('/login'), 3000)
}
}

updateSession()
}, [params.access_token, params.refresh_token, navigate])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security and error handling improvements needed.

Several issues need attention in the session handling logic:

  1. URL fragments containing tokens could be exposed in browser history
  2. Missing cleanup for the timeout
  3. Untyped error handling
  4. Production console.log

Consider these improvements:

 useEffect(() => {
   const updateSession = async () => {
     try {
       if (params.access_token && params.refresh_token) {
         const response = await supabase.auth.setSession({
           access_token: params.access_token,
           refresh_token: params.refresh_token
         })
       }
     } catch (error) {
-      console.log(error)
+      const errorMessage = error instanceof Error ? error.message : 'Invalid or expired reset link'
       setError('Invalid or expired reset link')
-      setTimeout(() => navigate('/login'), 3000)
+      const timeout = setTimeout(() => navigate('/login'), 3000)
+      return () => clearTimeout(timeout)
     }
   }

   updateSession()
+  // Clear URL fragment after reading tokens
+  window.history.replaceState(null, '', window.location.pathname)
 }, [params.access_token, params.refresh_token, navigate])

Also consider implementing a more secure token transfer mechanism, such as using state parameters or secure cookie-based approaches.

Committable suggestion was skipped due to low confidence.

if (inboxNotifications.length === 0) {
return <div className='p-4 text-center'>No notifications</div>
}
// ... NotificationsList component remains the same ...
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this line expected?

@giahung68 giahung68 merged commit 4224e13 into main Oct 28, 2024
This was referenced Nov 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants