diff --git a/src/app/(auth-pages)/auth/callback/route.ts b/src/app/(auth-pages)/auth/callback/route.ts
index f0c360a..a2ec361 100644
--- a/src/app/(auth-pages)/auth/callback/route.ts
+++ b/src/app/(auth-pages)/auth/callback/route.ts
@@ -2,76 +2,32 @@
import 'server-only';
import { createServerClient } from '@/utils/supabase';
-import { NextResponse } from 'next/server';
+import { type EmailOtpType } from '@supabase/supabase-js';
+import { type NextRequest } from 'next/server';
+import { redirect } from 'next/navigation';
-export const GET = async (request: Request) => {
- const requestUrl = new URL(request.url);
- const code = requestUrl.searchParams.get('code');
- const token = requestUrl.searchParams.get('token');
- const type = requestUrl.searchParams.get('type');
- const origin = requestUrl.origin;
- const redirectTo = requestUrl.searchParams.get('redirect_to')?.toString();
+export const GET = async (request: NextRequest) => {
+ const { searchParams } = new URL(request.url);
+ const token_hash = searchParams.get('token');
+ const type = searchParams.get('type') as EmailOtpType | null;
+ const redirectTo = searchParams.get('redirect_to') ?? '/';
- const supabase = await createServerClient();
-
- if (token && type) {
- try {
- if (type === 'signup') {
- // Confirm email signup
- const { error } = await supabase.auth.verifyOtp({
- token_hash: token,
- type: 'signup',
- });
-
- if (error) {
- console.error('Email confirmation error:', error);
- return NextResponse.redirect(`${origin}/sign-in?error=Invalid or expired confirmation link`);
- }
- } else if (type === 'recovery') {
- // Handle password recovery
- const { error } = await supabase.auth.verifyOtp({
- token_hash: token,
- type: 'recovery',
- });
-
- if (error) {
- console.error('Password recovery error:', error);
- return NextResponse.redirect(`${origin}/sign-in?error=Invalid or expired reset link`);
- } else {
- return NextResponse.redirect(`${origin}/reset-password`);
- }
- } else if (type === 'email_change') {
- // Handle email change
- const { error } = await supabase.auth.verifyOtp({
- token_hash: token,
- type: 'email_change',
- });
-
- if (error) {
- console.error('Email change error:', error);
- return NextResponse.redirect(`${origin}/profile?error=Invalid or expired email change link`);
- }
- }
- } catch (error) {
- console.error('Verification error:', error);
- return NextResponse.redirect(`${origin}/sign-in?error=Verification failed`);
+ if (token_hash && type) {
+ const supabase = await createServerClient();
+ const { error } = await supabase.auth.verifyOtp({
+ type,
+ token_hash,
+ });
+ if (!error) {
+ if (type === 'signup' || type === 'magiclink' || type === 'email')
+ return redirect('/');
+ if (type === 'recovery' || type === 'email_change')
+ return redirect('/profile');
+ if (type === 'invite')
+ return redirect('/sign-up');
+ else return redirect(`/?Could not identify type ${type as string}`)
}
+ else return redirect(`/?${error.message}`);
}
-
- // Handle code-based flow (OAuth, etc.)
- if (code) {
- await supabase.auth.exchangeCodeForSession(code);
- }
-
- // Handle redirect
- if (redirectTo) {
- try {
- new URL(redirectTo);
- return NextResponse.redirect(redirectTo);
- } catch {
- return NextResponse.redirect(`${origin}${redirectTo}`);
- }
- }
-
- return NextResponse.redirect(origin);
-}
+ return redirect('/');
+};
diff --git a/src/app/(auth-pages)/auth/callback/route.ts.bak b/src/app/(auth-pages)/auth/callback/route.ts.bak
index a3bade1..812d617 100644
--- a/src/app/(auth-pages)/auth/callback/route.ts.bak
+++ b/src/app/(auth-pages)/auth/callback/route.ts.bak
@@ -1,24 +1,77 @@
+'use server';
+
+import 'server-only';
import { createServerClient } from '@/utils/supabase';
-import { NextResponse } from 'next/server';
+import { type EmailOtpType } from '@supabase/supabase-js';
+import { type NextRequest, NextResponse } from 'next/server';
-export const GET = async (request: Request) => {
- // The `/auth/callback` route is required for the server-side auth flow implemented
- // by the SSR package. It exchanges an auth code for the user's session.
- // https://supabase.com/docs/guides/auth/server-side/nextjs
- const requestUrl = new URL(request.url);
- const code = requestUrl.searchParams.get('code');
- const origin = requestUrl.origin;
- const redirectTo = requestUrl.searchParams.get('redirect_to')?.toString();
+export const GET = async (request: NextRequest) => {
+ const { searchParams, origin } = new URL(request.url);
+ const code = searchParams.get('code');
+ const token = searchParams.get('token');
+ const type = searchParams.get('type') as EmailOtpType | null;
+ const redirectTo = searchParams.get('redirect_to')?.toString();
+ const supabase = await createServerClient();
+
+ if (token && type) {
+ try {
+ if (type === 'signup') {
+ // Confirm email signup
+ const { error } = await supabase.auth.verifyOtp({
+ token_hash: token,
+ type: 'signup',
+ });
+
+ if (error) {
+ console.error('Email confirmation error:', error);
+ return NextResponse.redirect(`${origin}/sign-in?error=Invalid or expired confirmation link`);
+ }
+ } else if (type === 'recovery') {
+ // Handle password recovery
+ const { error } = await supabase.auth.verifyOtp({
+ token_hash: token,
+ type: 'recovery',
+ });
+
+ if (error) {
+ console.error('Password recovery error:', error);
+ return NextResponse.redirect(`${origin}/sign-in?error=Invalid or expired reset link`);
+ } else {
+ return NextResponse.redirect(`${origin}/reset-password`);
+ }
+ } else if (type === 'email_change') {
+ // Handle email change
+ const { error } = await supabase.auth.verifyOtp({
+ token_hash: token,
+ type: 'email_change',
+ });
+
+ if (error) {
+ console.error('Email change error:', error);
+ return NextResponse.redirect(`${origin}/profile?error=Invalid or expired email change link`);
+ }
+ }
+ } catch (error) {
+ console.error('Verification error:', error);
+ return NextResponse.redirect(`${origin}/sign-in?error=Verification failed`);
+ }
+ }
+
+ // Handle code-based flow (OAuth, etc.)
if (code) {
- const supabase = await createServerClient();
await supabase.auth.exchangeCodeForSession(code);
}
+ // Handle redirect
if (redirectTo) {
- return NextResponse.redirect(`${origin}${redirectTo}`);
+ try {
+ new URL(redirectTo);
+ return NextResponse.redirect(redirectTo);
+ } catch {
+ return NextResponse.redirect(`${origin}${redirectTo}`);
+ }
}
- // URL to redirect to after sign up process completes
return NextResponse.redirect(origin);
}
diff --git a/src/app/(auth-pages)/layout.tsx b/src/app/(auth-pages)/layout.tsx
deleted file mode 100644
index a4dfbc4..0000000
--- a/src/app/(auth-pages)/layout.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-const Layout = async ({ children }: { children: React.ReactNode }) => {
- return (
-
- {children}
-
- );
-};
-export default Layout;
diff --git a/src/app/(auth-pages)/profile/page.tsx b/src/app/(auth-pages)/profile/page.tsx
index 094a11a..4b74787 100644
--- a/src/app/(auth-pages)/profile/page.tsx
+++ b/src/app/(auth-pages)/profile/page.tsx
@@ -5,7 +5,6 @@ import { useEffect } from 'react';
import { AvatarUpload, ProfileForm, ResetPasswordForm } from '@/components/default/profile';
import {
Card,
- CardContent,
CardHeader,
CardTitle,
CardDescription,
@@ -14,6 +13,7 @@ import {
import { Loader2 } from 'lucide-react';
import { resetPassword } from '@/lib/actions';
import { toast } from 'sonner';
+import { type Result } from '@/lib/actions';
const ProfilePage = () => {
const { profile, isLoading, isAuthenticated, updateProfile, refreshUserData } = useAuth();
@@ -44,19 +44,21 @@ const ProfilePage = () => {
}
};
- const handleResetPasswordSubmit = async (values: {
- password: string;
- confirmPassword: string;
- }) => {
+ const handleResetPasswordSubmit = async (
+ formData: FormData,
+ ): Promise> => {
try {
- const result = await resetPassword(values);
+ const result = await resetPassword(formData);
if (!result.success) {
toast.error(`Error resetting password: ${result.error}`)
+ return {success: false, error: result.error};
}
+ return {success: true, data: null};
} catch (error) {
toast.error(
`Error resetting password!: ${error as string ?? 'Unknown error'}`
);
+ return {success: false, error: 'Unknown error'};
}
}
@@ -87,7 +89,6 @@ const ProfilePage = () => {
Manage your personal information and how it appears to others
-
{isLoading && !profile ? (
@@ -101,7 +102,6 @@ const ProfilePage = () => {
)}
-
);
diff --git a/src/app/(auth-pages)/reset-password/page.tsx b/src/app/(auth-pages)/reset-password/page.tsx
deleted file mode 100644
index d332861..0000000
--- a/src/app/(auth-pages)/reset-password/page.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { resetPasswordFromEmail } from '@/lib/actions';
-import { FormMessage, type Message, SubmitButton } from '@/components/default';
-import { Input, Label } from '@/components/ui';
-import { getUser } from '@/lib/actions';
-import { redirect } from 'next/navigation';
-
-const ResetPassword = async (props: { searchParams: Promise }) => {
- const user = await getUser();
- if (!user.success) {
- redirect('/sign-in');
- }
- const searchParams = await props.searchParams;
- return (
-
- );
-};
-export default ResetPassword;
diff --git a/src/app/(auth-pages)/sign-in/page.tsx b/src/app/(auth-pages)/sign-in/page.tsx
index b0c9df0..1cb2954 100644
--- a/src/app/(auth-pages)/sign-in/page.tsx
+++ b/src/app/(auth-pages)/sign-in/page.tsx
@@ -1,15 +1,53 @@
'use client';
+
+import { z } from 'zod';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useForm } from 'react-hook-form';
+import {
+ Button,
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+ Input,
+ Label,
+} from '@/components/ui';
import Link from 'next/link';
import { signIn } from '@/lib/actions';
import { SubmitButton } from '@/components/default';
-import { Input, Label } from '@/components/ui';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/components/context/auth';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
+
+const formSchema = z.object({
+ email: z.string().email({
+ message: 'Please enter a valid email address.',
+ }),
+ password: z.string().min(8, {
+ message: 'Password must be at least 8 characters.',
+ }),
+})
const Login = () => {
const router = useRouter();
- const { isAuthenticated, refreshUserData } = useAuth();
+ const { isAuthenticated, isLoading, refreshUserData } = useAuth();
+ const [statusMessage, setStatusMessage] = useState('');
+
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ email: '',
+ password: '',
+ },
+ });
// Redirect if already authenticated
useEffect(() => {
@@ -18,46 +56,102 @@ const Login = () => {
}
}, [isAuthenticated, router]);
- const handleSignIn = async (formData: FormData) => {
- const result = await signIn(formData);
- if (result?.success) {
- await refreshUserData();
- router.push('/');
+ const handleSignIn = async (values: z.infer) => {
+ try {
+ const formData = new FormData();
+ formData.append('email', values.email);
+ formData.append('password', values.password);
+ const result = await signIn(formData);
+ if (result?.success) {
+ await refreshUserData();
+ form.reset();
+ router.push('');
+ } else {
+ setStatusMessage(`Error: ${result.error}`)
+ }
+ } catch (error) {
+ setStatusMessage(
+ `Error: ${error instanceof Error ? error.message : 'Could not sign in!'}`
+ );
}
};
return (
-
+
+
+
+
+
+
+
+
);
};
diff --git a/src/components/default/profile/AvatarUpload.tsx b/src/components/default/profile/AvatarUpload.tsx
index e93bd78..98db8ea 100644
--- a/src/components/default/profile/AvatarUpload.tsx
+++ b/src/components/default/profile/AvatarUpload.tsx
@@ -1,6 +1,6 @@
import { useFileUpload } from '@/lib/hooks/useFileUpload';
import { useAuth } from '@/components/context/auth';
-import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui';
+import { Avatar, AvatarFallback, AvatarImage, CardContent } from '@/components/ui';
import { Loader2, Pencil, Upload, User } from 'lucide-react';
type AvatarUploadProps = {
@@ -43,6 +43,8 @@ export const AvatarUpload = ({ onAvatarUploaded }: AvatarUploadProps) => {
};
return (
+
+
+
);
};
diff --git a/src/components/default/profile/ProfileForm.tsx b/src/components/default/profile/ProfileForm.tsx
index 79b4600..dab38ab 100644
--- a/src/components/default/profile/ProfileForm.tsx
+++ b/src/components/default/profile/ProfileForm.tsx
@@ -3,6 +3,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import {
Button,
+ CardContent,
Form,
FormControl,
FormDescription,
@@ -53,53 +54,55 @@ export const ProfileForm = ({onSubmit}: ProfileFormProps) => {
};
return (
-
-
+ />
+
+ (
+
+ Email
+
+
+
+
+ Your email address associated with your account.
+
+
+
+ )}
+ />
+
+
+
+
+
+
+
);
}
diff --git a/src/components/default/profile/ResetPasswordForm.tsx b/src/components/default/profile/ResetPasswordForm.tsx
index ab0cea9..a138088 100644
--- a/src/components/default/profile/ResetPasswordForm.tsx
+++ b/src/components/default/profile/ResetPasswordForm.tsx
@@ -2,7 +2,7 @@ import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import {
- Button,
+ CardContent,
CardDescription,
CardHeader,
CardTitle,
@@ -15,8 +15,10 @@ import {
FormMessage,
Input,
} from '@/components/ui';
-import { Loader2 } from 'lucide-react';
+import { SubmitButton } from '@/components/default';
import { useState } from 'react';
+import { type Result } from '@/lib/actions';
+import { FormMessage as Pw } from '@/components/default';
const formSchema = z
.object({
@@ -31,7 +33,7 @@ const formSchema = z
});
type ResetPasswordFormProps = {
- onSubmit: (values: z.infer) => Promise;
+ onSubmit: (formData: FormData) => Promise>;
message?: string;
};
@@ -50,14 +52,25 @@ export const ResetPasswordForm = ({
},
});
- const handleSubmit = async (values: z.infer) => {
+ const handleUpdatePassword = async (values: z.infer) => {
setIsLoading(true);
try {
- await onSubmit(values);
- setStatusMessage('Password updated successfully');
- form.reset();
+ // Convert form values to FormData for your server action
+ const formData = new FormData();
+ formData.append('password', values.password);
+ formData.append('confirmPassword', values.confirmPassword);
+
+ const result = await onSubmit(formData);
+ if (result?.success) {
+ setStatusMessage('Password updated successfully!');
+ form.reset(); // Clear the form on success
+ } else {
+ setStatusMessage('Error: Unable to update password!');
+ }
} catch (error) {
- setStatusMessage(error instanceof Error ? error.message : 'An error occurred');
+ setStatusMessage(
+ error instanceof Error ? error.message : 'Password was not updated!'
+ );
} finally {
setIsLoading(false);
}
@@ -65,15 +78,19 @@ export const ResetPasswordForm = ({
return (
-
- Change Password
-
- Update your password to keep your account secure
-
-
-
+
+ Change Password
+
+ Update your password to keep your account secure
+
+
+
+