Start working on Auth flow. Made email templates for auth

This commit is contained in:
2025-05-23 13:51:42 -05:00
parent 7f78bc7123
commit 44a1ea8a37
10 changed files with 320 additions and 243 deletions

View File

@ -2,7 +2,7 @@
import { useAuth } from '@/components/context/auth';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { AvatarUpload, ProfileForm } from '@/components/default/profile';
import { AvatarUpload, ProfileForm, ResetPasswordForm } from '@/components/default/profile';
import {
Card,
CardContent,
@ -12,6 +12,8 @@ import {
Separator,
} from '@/components/ui';
import { Loader2 } from 'lucide-react';
import { resetPassword } from '@/lib/actions';
import { toast } from 'sonner';
const ProfilePage = () => {
const { profile, isLoading, isAuthenticated, updateProfile, refreshUserData } = useAuth();
@ -32,12 +34,27 @@ const ProfilePage = () => {
full_name: string;
email: string;
}) => {
await updateProfile({
full_name: values.full_name,
email: values.email,
});
try {
await updateProfile({
full_name: values.full_name,
email: values.email,
});
} catch {
toast.error('Error updating profile!: ');
}
};
const handleResetPasswordSubmit = async (values: {
password: string;
confirmPassword: string;
}) => {
try {
await resetPassword(values);
} catch {
toast.error('Error resetting password!: ');
}
}
// Show loading state while checking authentication
if (isLoading) {
return (
@ -75,6 +92,8 @@ const ProfilePage = () => {
<AvatarUpload onAvatarUploaded={handleAvatarUploaded} />
<Separator />
<ProfileForm onSubmit={handleProfileSubmit} />
<Separator />
<ResetPasswordForm onSubmit={handleResetPasswordSubmit} />
</div>
)}
</CardContent>

View File

@ -1,8 +1,14 @@
import { resetPassword } from '@/lib/actions';
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<Message> }) => {
const user = await getUser();
if (!user.success) {
redirect('/sign-in');
}
const searchParams = await props.searchParams;
return (
<form className='flex flex-col w-full max-w-md p-4 gap-2 [&>input]:mb-4'>
@ -24,7 +30,7 @@ const ResetPassword = async (props: { searchParams: Promise<Message> }) => {
placeholder='Confirm password'
required
/>
<SubmitButton formAction={resetPassword}>Reset password</SubmitButton>
<SubmitButton formAction={resetPasswordFromEmail}>Reset password</SubmitButton>
<FormMessage message={searchParams} />
</form>
);

View File

@ -1,10 +1,14 @@
import Link from 'next/link';
import { signIn } from '@/lib/actions';
import { getUser, signIn } from '@/lib/actions';
import { FormMessage, type Message, SubmitButton } from '@/components/default';
import { Input, Label } from '@/components/ui';
import { redirect } from 'next/navigation';
import type { User } from '@/utils/supabase';
const Login = async (props: { searchParams: Promise<Message> }) => {
const searchParams = await props.searchParams;
const user = await getUser();
if (user.success) redirect('/profile');
return (
<form className='flex-1 flex flex-col min-w-64'>
<h1 className='text-2xl font-medium'>Sign in</h1>

View File

@ -2,9 +2,13 @@ import Link from 'next/link';
import { signUp } from '@/lib/actions';
import { FormMessage, type Message, SubmitButton } from '@/components/default';
import { Input, Label } from '@/components/ui';
import { redirect } from 'next/navigation';
import { getUser } from '@/lib/actions';
const SignUp = async (props: { searchParams: Promise<Message> }) => {
const searchParams = await props.searchParams;
const user = await getUser();
if (user.success) redirect('/profile');
if ('message' in searchParams) {
return (
<div

View File

@ -1,2 +1,3 @@
export * from './AvatarUpload';
export * from './ProfileForm';
export * from './ResetPasswordForm';

View File

@ -105,11 +105,21 @@ export const forgotPassword = async (formData: FormData) => {
);
};
export const resetPassword = async (formData: FormData) => {
const supabase = await createServerClient();
const password = formData.get('password') as string;
const confirmPassword = formData.get('confirmPassword') as string;
export const resetPassword = async (
formData: FormData | {password: string, confirmPassword: string}
) => {
let password = '';
let confirmPassword = '';
if (formData instanceof FormData) {
password = formData.get('password') as string;
confirmPassword = formData.get('confirmPassword') as string;
} else {
password = formData.password;
confirmPassword = formData.confirmPassword;
}
if (!password || !confirmPassword) {
encodedRedirect(
'error',
@ -118,6 +128,39 @@ export const resetPassword = async (formData: FormData) => {
);
}
const supabase = await createServerClient();
if (password !== confirmPassword) {
encodedRedirect('error', '/reset-password', 'Passwords do not match');
}
const { error } = await supabase.auth.updateUser({
password: password,
});
if (error) {
encodedRedirect('error', '/reset-password', 'Password update failed');
}
encodedRedirect('success', '/reset-password', 'Password updated');
};
export const resetPasswordFromEmail = async (formData: FormData) => {
const password = formData.get('password') as string;
const confirmPassword = formData.get('confirmPassword') as string;
if (!password || !confirmPassword) {
encodedRedirect(
'error',
'/reset-password',
'Password and confirm password are required',
);
}
const supabase = await createServerClient();
if (password !== confirmPassword) {
encodedRedirect('error', '/reset-password', 'Passwords do not match');
}

View File

@ -1,5 +1,5 @@
export { createClient } from './client';
export { createClient as createServerClient } from './server';
export { createServerClient } from './server';
export { updateSession } from './middleware';
export type * from './utils';
export type { Database } from './types';

View File

@ -1,11 +1,11 @@
import { createServerClient } from '@supabase/ssr';
import { createServerClient as CreateServerClient } from '@supabase/ssr';
import type { Database } from '@/utils/supabase/types';
import { cookies } from 'next/headers';
export const createClient = async () => {
export const createServerClient = async () => {
const cookieStore = await cookies();
return createServerClient<Database>(
return CreateServerClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{