Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import SnippetDetail from './components/SnippetDetail'
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

import { AuthProvider } from './providers/auth'

import { LOGOUT_PATH, ONBOARDING_PATH, LOGIN_PATH } from './constants/routes'
import { FORGET_PASSWORD_PATH, ONBOARDING_PATH, LOGIN_PATH, RESET_PASSWORD_PATH } from './constants/routes'
import { ResetPassword } from './components/ResetPassword'

const queryClient = new QueryClient()

Expand All @@ -23,6 +25,8 @@ export default function App(): ReactElement {
<Routes>
<Route path={ONBOARDING_PATH} element={<OnboardingPage />} />
<Route path={LOGIN_PATH} element={<LoginPage />} />
<Route path={FORGET_PASSWORD_PATH} element={<ForgetPassword />} />
<Route path={RESET_PASSWORD_PATH} element={<ResetPassword />} />
<Route element={<AuthenticatedLayout />}>
<Route path='/search' element={<SearchInterface />} />
<Route
Expand Down
110 changes: 110 additions & 0 deletions src/components/ForgetPassword.tsx
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
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>()


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
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.


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
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)

</div>
)}
</div>
</div>
</div>
)
}
76 changes: 33 additions & 43 deletions src/components/HeaderBar.tsx
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 ...
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?


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 = () => {
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

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
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.


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'>
Expand All @@ -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>
)
Expand Down
14 changes: 5 additions & 9 deletions src/components/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,12 @@ export default function LoginPage() {
}
})

// Redirect if user is already logged in
useEffect(() => {
if (user) {
navigate('/search')
}
}, [user, navigate])

// Form submission handler
const onSubmit = async (data: LoginFormData) => {
const { error } = await login(data.email, data.password)
if (error) {
Expand Down Expand Up @@ -65,7 +63,6 @@ export default function LoginPage() {

<form onSubmit={handleSubmit(onSubmit)} className='mt-8 space-y-6'>
<div className='space-y-4'>
{/* Email Input */}
<div>
<Input
{...register('email', {
Expand All @@ -83,7 +80,6 @@ export default function LoginPage() {
{errors.email && <p className='mt-1 text-sm text-red-500'>{errors.email.message}</p>}
</div>

{/* Password Input */}
<div className='relative'>
<Input
{...register('password', {
Expand All @@ -102,22 +98,23 @@ export default function LoginPage() {
</div>

<div className='flex items-center justify-end'>
<Button variant='link' className='h-auto p-0 text-blue-600'>
<Button
variant='link'
className='h-auto p-0 text-blue-600'
type='button'
onClick={() => navigate('/forget-password')}>
Forgot password?
</Button>
</div>
</div>

{/* Root error display */}
{errors.root && <p className='text-sm text-red-500'>{errors.root.message}</p>}

{/* Submit Button */}
<Button type='submit' className='h-12 w-full bg-[#005EF4] hover:bg-[#004ED1]' disabled={isSubmitting}>
{isSubmitting ? 'Signing in...' : 'Continue'}
</Button>
</form>

{/* Divider */}
<div className='mt-6'>
<div className='relative'>
<div className='absolute inset-0 flex items-center'>
Expand All @@ -128,7 +125,6 @@ export default function LoginPage() {
</div>
</div>

{/* Google Sign In Button */}
<div className='mt-6'>
<Button onClick={handleGoogleSignIn} variant='outline' className='h-12 w-full' disabled={isSubmitting}>
<img
Expand Down
2 changes: 2 additions & 0 deletions src/components/OnboardingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Upload, Loader2 } from 'lucide-react'
import supabase from '../lib/supabase'
import { useNavigate } from 'react-router-dom'
import PublicHeader from './PublicHeader'

type FormData = {
email: string
Expand Down Expand Up @@ -149,6 +150,7 @@ export default function OnboardingPage() {

return (
<div className='flex min-h-screen flex-col'>
<PublicHeader />
<div className='flex flex-grow items-center justify-center'>
<Card className='w-full max-w-md border-0 shadow-none'>
<CardHeader className='text-center'>
Expand Down
Loading