-
Notifications
You must be signed in to change notification settings - Fork 0
[f] 080 - Reset and Forget password page #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
deae8d5
f471cd2
ca04c8a
bb06477
21b2281
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,110 @@ | ||||||||||||||||||||||||||||||||||||||||||
| import { useState } from 'react' | ||||||||||||||||||||||||||||||||||||||||||
| import { useForm, SubmitHandler } from 'react-hook-form' | ||||||||||||||||||||||||||||||||||||||||||
| import { Input } from '@/components/ui/input' | ||||||||||||||||||||||||||||||||||||||||||
| import { Button } from '@/components/ui/button' | ||||||||||||||||||||||||||||||||||||||||||
| import { Loader2, CheckCircle2 } from 'lucide-react' // Add CheckCircle2 import | ||||||||||||||||||||||||||||||||||||||||||
| import supabase from '../lib/supabase' | ||||||||||||||||||||||||||||||||||||||||||
| import PublicHeader from './PublicHeader' | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| type FormData = { | ||||||||||||||||||||||||||||||||||||||||||
| email: string | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| 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>() | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+13
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider optimizing state management. The 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
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| 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) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+24
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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)
}
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||
| <div className='flex min-h-screen flex-col bg-white'> | ||||||||||||||||||||||||||||||||||||||||||
| <PublicHeader /> | ||||||||||||||||||||||||||||||||||||||||||
| <div className='flex flex-grow items-center justify-center px-4'> | ||||||||||||||||||||||||||||||||||||||||||
| <div className='w-full max-w-lg space-y-6'> | ||||||||||||||||||||||||||||||||||||||||||
| <h1 className='text-center text-2xl font-semibold text-gray-900'>Reset Password</h1> | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| {!isSuccess ? ( | ||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||
| <p className='text-center text-base text-gray-600'> | ||||||||||||||||||||||||||||||||||||||||||
| To reset your password, please enter the email of your VERDAD account. | ||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| <form onSubmit={handleSubmit(onSubmit)} className='space-y-4'> | ||||||||||||||||||||||||||||||||||||||||||
| <Input | ||||||||||||||||||||||||||||||||||||||||||
| id='email' | ||||||||||||||||||||||||||||||||||||||||||
| type='email' | ||||||||||||||||||||||||||||||||||||||||||
| 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>} | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||||||||
| type='submit' | ||||||||||||||||||||||||||||||||||||||||||
| className='w-full rounded-md bg-blue-600 py-2 text-white hover:bg-blue-700 focus:outline-none disabled:opacity-70' | ||||||||||||||||||||||||||||||||||||||||||
| disabled={isLoading}> | ||||||||||||||||||||||||||||||||||||||||||
| {isLoading ? ( | ||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||
| <Loader2 className='mr-2 h-4 w-4 animate-spin' /> | ||||||||||||||||||||||||||||||||||||||||||
| Sending... | ||||||||||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||
| 'Send Reset Password Link' | ||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||
| </form> | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| <div className='text-center'> | ||||||||||||||||||||||||||||||||||||||||||
| <a href='/login' className='text-sm text-blue-600 hover:underline'> | ||||||||||||||||||||||||||||||||||||||||||
| Go to Login | ||||||||||||||||||||||||||||||||||||||||||
| </a> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||
| <div className='flex flex-col space-y-6'> | ||||||||||||||||||||||||||||||||||||||||||
| <div className='flex'> | ||||||||||||||||||||||||||||||||||||||||||
| <div className='flex justify-center'> | ||||||||||||||||||||||||||||||||||||||||||
| <CheckCircle2 className='h-12 w-12 text-green-500' /> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| <p className='text-center text-base text-gray-600'> | ||||||||||||||||||||||||||||||||||||||||||
| We've sent an email to {email.replace(/(.{2})(.*)(?=@)/, (_, a, b) => a + '*'.repeat(b.length))} with | ||||||||||||||||||||||||||||||||||||||||||
| password reset instructions. Please check your email. | ||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| <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> | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+99
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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>
🧰 Tools🪛 Biome
|
||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,49 +1,29 @@ | ||
| import React, { useState } from 'react' | ||
| import { Link } from 'react-router-dom' | ||
| import { Moon, Bell } from 'lucide-react' | ||
| import { Moon, Bell, LogOut } from 'lucide-react' // Add LogOut icon | ||
| import { Button } from '@/components/ui/button' | ||
| import { useInboxNotifications } from '@liveblocks/react' | ||
| import { InboxNotification } from '@liveblocks/react-ui' | ||
| import { User } from '@supabase/supabase-js' | ||
| import { InboxPopover } from './InboxPopover' | ||
| import { useAuth } from '@/providers/auth' | ||
| import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' // Import dropdown components | ||
| import supabase from '@/lib/supabase' | ||
|
|
||
| interface HeaderBarProps { | ||
| user: User | ||
| } | ||
|
|
||
| const NotificationsList = () => { | ||
| const { inboxNotifications, error, isLoading } = useInboxNotifications() | ||
|
|
||
| if (isLoading) { | ||
| return <div className='p-4 text-center'>Loading notifications...</div> | ||
| } | ||
|
|
||
| if (error) { | ||
| return <div className='p-4 text-center text-red-500'>Error loading notifications: {error.message}</div> | ||
| } | ||
|
|
||
| if (inboxNotifications.length === 0) { | ||
| return <div className='p-4 text-center'>No notifications</div> | ||
| } | ||
| // ... NotificationsList component remains the same ... | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this line expected? |
||
|
|
||
| return ( | ||
| <DropdownMenuContent className='max-h-96 w-80 overflow-y-auto'> | ||
| {inboxNotifications.map(notification => ( | ||
| <InboxNotification key={notification.id} inboxNotification={notification} /> | ||
| ))} | ||
| </DropdownMenuContent> | ||
| ) | ||
| } | ||
|
|
||
| const HeaderBar: React.FC<HeaderBarProps> = () => { | ||
| const HeaderBar: React.FC = () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
🔗 Analysis chainVerify 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 executedThe 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 |
||
| const [isOpen, setIsOpen] = useState(false) | ||
| const { user } = useAuth() | ||
|
|
||
| const getInitials = (email: string) => { | ||
| return email.split('@')[0].slice(0, 2).toUpperCase() | ||
| } | ||
|
|
||
| const handleLogout = async () => { | ||
| await supabase.auth.signOut() | ||
| } | ||
|
Comment on lines
+23
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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}
+ >
|
||
|
|
||
| return ( | ||
| <header className='flex items-center justify-between border-b border-blue-600 bg-blue-50 px-8 py-2'> | ||
| <Link to='/' className='no-underline'> | ||
|
|
@@ -56,20 +36,30 @@ const HeaderBar: React.FC<HeaderBarProps> = () => { | |
| <Button variant='ghost' size='icon' className='h-8 w-8 p-0'> | ||
| <Moon className='h-6 w-6 text-blue-600 hover:bg-gray-50' /> | ||
| </Button> | ||
| <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'> | ||
| {getInitials(user?.email || '')} | ||
| </div> | ||
| )} | ||
| </Button> | ||
| <DropdownMenu> | ||
| <DropdownMenuTrigger asChild> | ||
| <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'> | ||
| {getInitials(user?.email || '')} | ||
| </div> | ||
| )} | ||
| </Button> | ||
| </DropdownMenuTrigger> | ||
| <DropdownMenuContent align='end' className='w-48'> | ||
| <DropdownMenuItem className='cursor-pointer' onClick={handleLogout}> | ||
| <LogOut className='mr-2 h-4 w-4' /> | ||
| <span>Log out</span> | ||
| </DropdownMenuItem> | ||
| </DropdownMenuContent> | ||
| </DropdownMenu> | ||
| </div> | ||
| </header> | ||
| ) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
Or:
Also applies to: 16-16