Skip to content

Commit 5d42c12

Browse files
authored
Feature/implement all auth features (#132)
* feat: update environment configuration and redirect paths for user authentication - Added API and NextAuth configuration to env.example for better setup clarity. - Changed redirect path for authenticated users from '/dashboard' to '/user' in middleware and sign-in logic. - Updated AuthNav component to link to the new '/user' route. - Simplified the Home page by adding a button to navigate to the user dashboard. - Adjusted API base URL reference in the api.ts file for consistency. * feat: enhance authentication flow and UI components - Updated .gitignore to include .env.example for better environment setup. - Added debug logging in auth.ts for improved error tracking during user login. - Refactored registration API to use firstName and lastName instead of name. - Introduced new AuthLayout and LoginForm components for a cleaner authentication UI. - Implemented BoundlessButton for consistent button styling across forms. - Added new SVG assets for branding and UI enhancements. - Improved error handling in auth-store and utility functions for better user feedback. * refactor: streamline error handling in authentication forms - Removed console error logging in CreatePasswordForm, LoginForm, and auth-store for cleaner code. - Simplified error handling by omitting error parameters in catch blocks, enhancing user feedback during account creation and login processes. - Added a comment to clarify type handling in SignupForm error management.
1 parent b507c5e commit 5d42c12

29 files changed

Lines changed: 2180 additions & 875 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ yarn-error.log*
3232

3333
# env files (can opt-in for committing if needed)
3434
.env*
35+
!.env.example
36+
37+
3538

3639
# vercel
3740
.vercel

.husky/commit-msg

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,33 @@
22

33
# Conventional commit message format
44
# Format: <type>(<scope>): <description>
5-
# Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert
5+
# Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert, merge
6+
# Also allows Git's standard merge commit format: "Merge branch '...' of ... into ..."
67

7-
commit_regex='^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?: .{1,50}'
8+
# Regex for conventional commits
9+
conventional_regex='^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert|merge)(\(.+\))?: .{1,50}'
10+
11+
# Regex for Git merge commits
12+
merge_regex='^Merge branch .* of .* into .*'
13+
14+
# Combined regex - either conventional or merge format
15+
commit_regex="($conventional_regex|$merge_regex)"
816

917
if ! grep -qE "$commit_regex" "$1"; then
1018
echo "❌ Invalid commit message format."
11-
echo "✅ Please use conventional commit format:"
12-
echo " <type>(<scope>): <description>"
19+
echo "✅ Please use either:"
20+
echo " 1. Conventional commit format: <type>(<scope>): <description>"
21+
echo " 2. Git's standard merge format: Merge branch '...' of ... into ..."
1322
echo ""
1423
echo "📝 Examples:"
1524
echo " feat: add new wallet connection feature"
1625
echo " fix(wallet): resolve persistence issue"
1726
echo " docs: update README with setup instructions"
1827
echo " style: format code with prettier"
1928
echo " refactor(components): improve wallet button"
29+
echo " Merge branch 'main' of https://github.com/user/repo into feature/branch"
2030
echo ""
21-
echo "🔧 Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
31+
echo "🔧 Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert, merge"
2232
exit 1
2333
fi
2434

app/api/auth/register/route.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@ import { NextResponse } from 'next/server';
33
import { z } from 'zod';
44

55
const userSchema = z.object({
6-
name: z.string().min(2, 'Name must be at least 2 characters'),
6+
firstName: z.string().min(2, 'First name must be at least 2 characters'),
7+
lastName: z.string().min(2, 'Last name must be at least 2 characters'),
78
email: z.string().email('Invalid email address'),
89
password: z.string().min(8, 'Password must be at least 8 characters'),
910
});
1011

1112
export async function POST(req: Request) {
1213
try {
1314
const body = await req.json();
14-
const { name, email, password } = userSchema.parse(body);
15+
const { firstName, lastName, email, password } = userSchema.parse(body);
1516
const registerData = {
1617
email,
1718
password,
18-
firstName: name,
19-
lastName: name,
19+
firstName,
20+
lastName,
2021
username: email.split('@')[0],
2122
};
2223
const response = await register(registerData);
@@ -25,6 +26,25 @@ export async function POST(req: Request) {
2526
if (error instanceof z.ZodError) {
2627
return NextResponse.json({ message: error.message }, { status: 400 });
2728
}
29+
30+
if (
31+
typeof error === 'object' &&
32+
error !== null &&
33+
'message' in error &&
34+
'status' in error
35+
) {
36+
const serverError = error;
37+
const errorMessage =
38+
serverError?.message || 'An unexpected error occurred';
39+
const statusCode =
40+
typeof serverError?.status === 'number' ? serverError.status : 400;
41+
42+
return NextResponse.json(
43+
{ message: errorMessage },
44+
{ status: statusCode }
45+
);
46+
}
47+
2848
const errorMessage =
2949
error instanceof Error ? error.message : 'An unexpected error occurred';
3050
return NextResponse.json({ message: errorMessage }, { status: 500 });

app/auth/forgot-password/page.tsx

Lines changed: 108 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -2,126 +2,153 @@
22

33
import { useState } from 'react';
44
import Link from 'next/link';
5-
import { Button } from '@/components/ui/button';
65
import { Input } from '@/components/ui/input';
7-
import { Label } from '@/components/ui/label';
6+
import { Mail, ArrowLeft } from 'lucide-react';
7+
import Image from 'next/image';
88
import {
9-
Card,
10-
CardContent,
11-
CardDescription,
12-
CardHeader,
13-
CardTitle,
14-
} from '@/components/ui/card';
15-
import { Alert, AlertDescription } from '@/components/ui/alert';
16-
import { Loader2, Mail, ArrowLeft } from 'lucide-react';
9+
Form,
10+
FormControl,
11+
FormField,
12+
FormItem,
13+
FormLabel,
14+
FormMessage,
15+
} from '@/components/ui/form';
16+
import { useForm } from 'react-hook-form';
17+
import { zodResolver } from '@hookform/resolvers/zod';
18+
import { z } from 'zod';
19+
import { BoundlessButton } from '@/components/buttons';
20+
21+
const forgotPasswordSchema = z.object({
22+
email: z.string().email({
23+
message: 'Please enter a valid email address',
24+
}),
25+
});
26+
27+
type ForgotPasswordFormData = z.infer<typeof forgotPasswordSchema>;
1728

1829
export default function ForgotPasswordPage() {
19-
const [email, setEmail] = useState('');
2030
const [isLoading, setIsLoading] = useState(false);
21-
const [error, setError] = useState('');
22-
const [success, setSuccess] = useState('');
31+
const [successMessage, setSuccessMessage] = useState('');
32+
const [errorMessage, setErrorMessage] = useState('');
33+
34+
const form = useForm<ForgotPasswordFormData>({
35+
resolver: zodResolver(forgotPasswordSchema),
36+
defaultValues: {
37+
email: '',
38+
},
39+
});
2340

24-
const handleSubmit = async (e: React.FormEvent) => {
25-
e.preventDefault();
41+
const handleSubmit = async (data: ForgotPasswordFormData) => {
2642
setIsLoading(true);
27-
setError('');
28-
setSuccess('');
43+
setSuccessMessage('');
44+
setErrorMessage('');
2945

3046
try {
3147
const response = await fetch('/api/auth/forgot-password', {
3248
method: 'POST',
3349
headers: {
3450
'Content-Type': 'application/json',
3551
},
36-
body: JSON.stringify({ email }),
52+
body: JSON.stringify({ email: data.email }),
3753
});
3854

39-
const data = await response.json();
55+
const res = await response.json();
4056

4157
if (response.ok) {
42-
setSuccess(
58+
setSuccessMessage(
4359
'Password reset instructions have been sent to your email address.'
4460
);
45-
setEmail('');
61+
form.reset();
4662
} else {
47-
setError(data.message || 'Failed to send reset email');
63+
setErrorMessage(res.message || 'Failed to send reset email');
4864
}
4965
} catch {
50-
setError('An unexpected error occurred');
66+
setErrorMessage('An unexpected error occurred');
5167
} finally {
5268
setIsLoading(false);
5369
}
5470
};
5571

5672
return (
57-
<div className='min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8'>
73+
<div className='min-h-screen flex items-center justify-center bg-background py-12 px-4 sm:px-6 lg:px-16'>
74+
<div className='absolute top-16 left-16'>
75+
<Image
76+
src='/auth/logo.svg'
77+
alt='logo'
78+
width={123}
79+
height={22}
80+
className='object-cover'
81+
/>
82+
</div>
5883
<div className='max-w-md w-full space-y-8'>
5984
<div className='text-center'>
60-
<h2 className='mt-6 text-3xl font-extrabold text-gray-900'>
61-
Forgot your password?
85+
<h2 className='mt-6 text-[40px] font-medium text-white'>
86+
Reset Password
6287
</h2>
63-
<p className='mt-2 text-sm text-gray-600'>
64-
Enter your email address and we'll send you a link to reset your
65-
password.
88+
<p className='mt-2 text-[#D9D9D9]'>
89+
Enter the email address you used when you joined, and we'll send you
90+
instructions to reset your password.
6691
</p>
6792
</div>
6893

69-
<Card>
70-
<CardHeader>
71-
<CardTitle>Reset Password</CardTitle>
72-
<CardDescription>
73-
We'll send you an email with instructions to reset your password
74-
</CardDescription>
75-
</CardHeader>
76-
<CardContent className='space-y-4'>
77-
{error && (
78-
<Alert variant='destructive'>
79-
<AlertDescription>
80-
{typeof error === 'string' ? error : 'An error occurred'}
81-
</AlertDescription>
82-
</Alert>
83-
)}
94+
{successMessage && (
95+
<div className='text-sm text-green-500 '>{successMessage}</div>
96+
)}
8497

85-
{success && (
86-
<Alert>
87-
<AlertDescription>{success}</AlertDescription>
88-
</Alert>
89-
)}
98+
{errorMessage && (
99+
<div className='text-sm text-red-500'>{errorMessage}</div>
100+
)}
90101

91-
<form onSubmit={handleSubmit} className='space-y-4'>
92-
<div className='space-y-2'>
93-
<Label htmlFor='email'>Email</Label>
94-
<div className='relative'>
95-
<Mail className='absolute left-3 top-3 h-4 w-4 text-gray-400' />
96-
<Input
97-
id='email'
98-
type='email'
99-
value={email}
100-
onChange={e => setEmail(e.target.value)}
101-
placeholder='Enter your email'
102-
className='pl-10'
103-
required
104-
/>
105-
</div>
106-
</div>
102+
<Form {...form}>
103+
<form
104+
onSubmit={form.handleSubmit(handleSubmit)}
105+
className='space-y-4'
106+
>
107+
<FormField
108+
control={form.control}
109+
name='email'
110+
render={({ field }) => (
111+
<FormItem>
112+
<FormLabel className='text-white text-xs font-medium'>
113+
Email
114+
</FormLabel>
115+
<FormControl>
116+
<div className='relative'>
117+
<Mail className='absolute left-3 top-3 h-4 w-4 text-[#B5B5B5]' />
118+
<Input
119+
{...field}
120+
type='email'
121+
placeholder='Enter your email'
122+
className='text-white placeholder:text-[#B5B5B5] border-[#2B2B2B] bg-[#1C1C1C] focus-visible:ring-0 focus-visible:ring-offset-0 caret-white w-full pl-10'
123+
/>
124+
</div>
125+
</FormControl>
126+
<FormMessage />
127+
</FormItem>
128+
)}
129+
/>
107130

108-
<Button type='submit' className='w-full' disabled={isLoading}>
109-
{isLoading && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
110-
Send Reset Link
111-
</Button>
112-
</form>
131+
<BoundlessButton
132+
type='submit'
133+
className='w-full'
134+
disabled={isLoading}
135+
fullWidth
136+
loading={isLoading}
137+
>
138+
Send Reset Link
139+
</BoundlessButton>
140+
</form>
141+
</Form>
113142

114-
<div className='text-center'>
115-
<Link
116-
href='/auth/signin'
117-
className='inline-flex items-center text-sm text-blue-600 hover:text-blue-500'
118-
>
119-
<ArrowLeft className='mr-1 h-4 w-4' />
120-
Back to sign in
121-
</Link>
122-
</div>
123-
</CardContent>
124-
</Card>
143+
<div className='text-center'>
144+
<Link
145+
href='/auth/signin'
146+
className='inline-flex items-center text-sm text-primary hover:text-primary/80'
147+
>
148+
<ArrowLeft className='mr-1 h-4 w-4' />
149+
Back to sign in
150+
</Link>
151+
</div>
125152
</div>
126153
</div>
127154
);

0 commit comments

Comments
 (0)