Skip to content

Commit 49fdf0a

Browse files
committed
feat: implement authentication flow with new components
- Added ForgotPassword, LoginWrapper, and SignupWrapper components for enhanced user authentication experience. - Updated AuthPage to include loading states and improved layout. - Enhanced styling in various components for better visual consistency. - Fixed minor issues in SVG attributes for better compatibility.
1 parent 6406dad commit 49fdf0a

11 files changed

Lines changed: 889 additions & 485 deletions

File tree

app/auth/forgot-password/page.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use client';
2+
3+
import React, { useState } from 'react';
4+
import AuthLoadingState from '@/components/auth/AuthLoadingState';
5+
import ForgotPasswordWrapper from '@/components/auth/ForgotPasswordWrapper';
6+
7+
const ForgotPassword = () => {
8+
const [loadingState, setLoadingState] = useState(false);
9+
10+
return (
11+
<>
12+
{loadingState && <AuthLoadingState message='Sending reset link...' />}
13+
<div className='relative z-10 flex min-h-screen items-center justify-center p-4'>
14+
<div className='w-full max-w-[500px]'>
15+
<div className='group relative rounded-2xl border border-white/20 bg-gradient-to-br from-white/10 via-white/5 to-transparent p-10 shadow-2xl backdrop-blur-xl transition-all duration-200 hover:border-2 hover:border-white/30 hover:shadow-[0_25px_50px_-12px_rgba(0,0,0,0.5)]'>
16+
<div className='pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-br from-white/5 via-transparent to-white/10 opacity-50'></div>
17+
<div className='pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-br from-white/10 via-transparent to-transparent opacity-30'></div>
18+
19+
<div className='relative z-10'>
20+
<ForgotPasswordWrapper setLoadingState={setLoadingState} />
21+
</div>
22+
</div>
23+
</div>
24+
</div>
25+
</>
26+
);
27+
};
28+
29+
export default ForgotPassword;

app/auth/page.tsx

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
import { useState } from 'react';
44
import { useSearchParams } from 'next/navigation';
55
import { AuthModeNav } from '@/components/auth/AuthModeNav';
6+
import LoginWrapper from '@/components/auth/LoginWrapper';
7+
import SignupWrapper from '@/components/auth/SignupWrapper';
8+
import AuthLoadingState from '@/components/auth/AuthLoadingState';
69

710
export default function AuthPage() {
11+
const [loadingState, setLoadingState] = useState(false);
812
const searchParams = useSearchParams();
13+
914
const mode = searchParams.get('mode') || 'signin';
1015
const [currentMode, setCurrentMode] = useState<'signin' | 'signup'>(
1116
mode as 'signin' | 'signup'
@@ -16,17 +21,32 @@ export default function AuthPage() {
1621
};
1722

1823
return (
19-
<div className='relative z-10 flex min-h-screen items-center justify-center p-4'>
20-
<div className='w-full max-w-md'>
21-
<div className='rounded-2xl border border-white/10 bg-black/20 p-8 backdrop-blur-sm'>
22-
<AuthModeNav
23-
currentMode={currentMode}
24-
onModeChange={handleModeChange}
25-
/>
26-
27-
<div className='mt-8'></div>
24+
<>
25+
{loadingState && <AuthLoadingState message='Signing in...' />}
26+
<div className='relative z-10 flex min-h-screen items-center justify-center p-4'>
27+
<div className='w-full max-w-[600px]'>
28+
<div className='group relative rounded-2xl border border-white/20 bg-gradient-to-br from-white/10 via-white/5 to-transparent p-10 shadow-2xl backdrop-blur-xl transition-all duration-500 hover:border-2 hover:border-white/30 hover:shadow-[0_25px_50px_-12px_rgba(0,0,0,0.5)]'>
29+
<div className='pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-br from-white/5 via-transparent to-white/10 opacity-50'></div>
30+
31+
<div className='pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-br from-white/10 via-transparent to-transparent opacity-30'></div>
32+
33+
<div className='relative z-10'>
34+
<AuthModeNav
35+
currentMode={currentMode}
36+
onModeChange={handleModeChange}
37+
/>
38+
39+
<div className='mt-2'>
40+
{currentMode === 'signin' ? (
41+
<LoginWrapper setLoadingState={setLoadingState} />
42+
) : (
43+
<SignupWrapper setLoadingState={setLoadingState} />
44+
)}
45+
</div>
46+
</div>
47+
</div>
2848
</div>
2949
</div>
30-
</div>
50+
</>
3151
);
3252
}

app/globals.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,12 @@ input[type='number'] {
447447
background-image: url('/landing/about/Abstract_03md.png');
448448
}
449449
}
450+
451+
input:-webkit-autofill,
452+
input:-webkit-autofill:hover,
453+
input:-webkit-autofill:focus,
454+
input:-webkit-autofill:active {
455+
-webkit-box-shadow: 0 0 0 30px your_desired_color inset !important;
456+
-webkit-text-fill-color: white !important; /* Optional: Customize text color */
457+
transition: background-color 5000s ease-in-out 0s; /* Optional: Smooth transition */
458+
}

components/auth/AnimatedAuthLayout.tsx

Lines changed: 257 additions & 257 deletions
Large diffs are not rendered by default.

components/auth/AuthLoadingState.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const AuthLoadingState = ({
1515
<div
1616
className={cn(
1717
'fixed inset-0 z-[999999] flex items-center justify-center',
18-
'bg-black/90 backdrop-blur-sm',
18+
'bg-[rgba(28,28,28,0.52)] backdrop-blur-[50px]',
1919
className
2020
)}
2121
>
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
'use client';
2+
3+
import { zodResolver } from '@hookform/resolvers/zod';
4+
import { useState, useCallback } from 'react';
5+
import { useForm } from 'react-hook-form';
6+
import { toast } from 'sonner';
7+
import z from 'zod';
8+
import { Mail, ArrowLeft } from 'lucide-react';
9+
import Link from 'next/link';
10+
import { BoundlessButton } from '../buttons';
11+
import {
12+
Form,
13+
FormControl,
14+
FormField,
15+
FormItem,
16+
FormLabel,
17+
FormMessage,
18+
} from '../ui/form';
19+
import { Input } from '../ui/input';
20+
import { forgotPassword } from '@/lib/api/auth';
21+
22+
const forgotPasswordSchema = z.object({
23+
email: z.string().email({
24+
message: 'Please enter a valid email address',
25+
}),
26+
});
27+
28+
type ForgotPasswordFormData = z.infer<typeof forgotPasswordSchema>;
29+
30+
interface ForgotPasswordWrapperProps {
31+
setLoadingState: (isLoading: boolean) => void;
32+
}
33+
34+
const ForgotPasswordWrapper = ({
35+
setLoadingState,
36+
}: ForgotPasswordWrapperProps) => {
37+
const [isLoading, setIsLoading] = useState(false);
38+
const [isSuccess, setIsSuccess] = useState(false);
39+
40+
const form = useForm<ForgotPasswordFormData>({
41+
resolver: zodResolver(forgotPasswordSchema),
42+
defaultValues: {
43+
email: '',
44+
},
45+
});
46+
47+
const handleSubmit = useCallback(
48+
async (data: ForgotPasswordFormData) => {
49+
setIsLoading(true);
50+
setLoadingState(true);
51+
52+
try {
53+
// const response = await fetch('/api/auth/forgot-password', {
54+
// method: 'POST',
55+
// headers: {
56+
// 'Content-Type': 'application/json',
57+
// },
58+
// body: JSON.stringify({ email: data.email }),
59+
// });
60+
const response = await forgotPassword({ email: data.email });
61+
62+
const result = response;
63+
console.log(result);
64+
65+
if (response) {
66+
setIsSuccess(true);
67+
toast.success('Password reset instructions sent to your email');
68+
form.reset();
69+
} else {
70+
form.setError('root', {
71+
type: 'manual',
72+
message: result.message || 'Failed to send reset email',
73+
});
74+
toast.error(result.message || 'Failed to send reset email');
75+
}
76+
} catch {
77+
form.setError('root', {
78+
type: 'manual',
79+
message: 'An unexpected error occurred. Please try again.',
80+
});
81+
toast.error('An unexpected error occurred');
82+
} finally {
83+
setIsLoading(false);
84+
setLoadingState(false);
85+
}
86+
},
87+
[form, setLoadingState]
88+
);
89+
90+
if (isSuccess) {
91+
return (
92+
<div className='space-y-6'>
93+
<div className='text-center'>
94+
<div className='mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-green-500/20'>
95+
<Mail className='h-8 w-8 text-green-500' />
96+
</div>
97+
<h2 className='text-2xl font-semibold text-white'>
98+
Check Your Email
99+
</h2>
100+
<p className='mt-2 text-sm text-[#D9D9D9]'>
101+
We've sent password reset instructions to your email address.
102+
</p>
103+
</div>
104+
105+
<div className='text-center'>
106+
<Link
107+
href='/auth'
108+
className='text-primary hover:text-primary/80 inline-flex items-center text-sm'
109+
>
110+
<ArrowLeft className='mr-1 h-4 w-4' />
111+
Back to sign in
112+
</Link>
113+
</div>
114+
</div>
115+
);
116+
}
117+
118+
return (
119+
<div className='space-y-6'>
120+
<div className=''>
121+
<h2 className='text-[40px] leading-[120%] font-medium tracking-[-1.6px] text-white'>
122+
Reset Password
123+
</h2>
124+
<p className='mt-2 text-[16px] leading-relaxed tracking-[-0.64px] text-[#D9D9D9]'>
125+
Enter the email address you used when you joined, and we'll send you
126+
instructions to reset your password.
127+
</p>
128+
</div>
129+
130+
<Form {...form}>
131+
<form onSubmit={form.handleSubmit(handleSubmit)} className='space-y-4'>
132+
<FormField
133+
control={form.control}
134+
name='email'
135+
render={({ field }) => (
136+
<FormItem>
137+
<FormLabel className='text-xs font-medium text-white'>
138+
Email
139+
</FormLabel>
140+
<FormControl>
141+
<div className='relative'>
142+
<Mail className='absolute top-3 left-3 h-4 w-4 text-[#B5B5B5]' />
143+
<Input
144+
{...field}
145+
type='email'
146+
placeholder='Enter your email'
147+
className='w-full border-[#2B2B2B] bg-[#1C1C1C] pl-10 text-white caret-white placeholder:text-[#B5B5B5] focus-visible:ring-0 focus-visible:ring-offset-0'
148+
/>
149+
</div>
150+
</FormControl>
151+
<FormMessage />
152+
</FormItem>
153+
)}
154+
/>
155+
156+
{form.formState.errors.root && (
157+
<div className='text-sm text-red-500'>
158+
{form.formState.errors.root.message}
159+
</div>
160+
)}
161+
162+
<BoundlessButton
163+
type='submit'
164+
className='w-full'
165+
disabled={isLoading || !form.formState.isValid}
166+
fullWidth
167+
loading={isLoading}
168+
>
169+
Send Reset Link
170+
</BoundlessButton>
171+
</form>
172+
</Form>
173+
174+
<div className='text-center'>
175+
<Link
176+
href='/auth'
177+
className='text-primary hover:text-primary/80 inline-flex items-center text-sm'
178+
>
179+
<ArrowLeft className='mr-1 h-4 w-4' />
180+
Back to sign in
181+
</Link>
182+
</div>
183+
</div>
184+
);
185+
};
186+
187+
export default ForgotPasswordWrapper;

components/auth/LoginForm.tsx

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,33 +44,34 @@ const LoginForm = ({
4444
return (
4545
<>
4646
<div className='space-y-2'>
47-
<h2 className='mb-3 text-center text-2xl font-medium text-white md:text-left lg:text-[40px]'>
48-
Sign in
49-
</h2>
5047
<p className='text-center text-sm leading-relaxed text-[#D9D9D9] md:text-left lg:text-base'>
5148
Sign in to manage campaigns, apply for grants, and track your funding
52-
progress — all in one dashboard.
49+
progress.
5350
</p>
5451
</div>
55-
<div className='space-y-6'>
52+
<div className='mt-6 space-y-6'>
5653
<BoundlessButton
5754
fullWidth
58-
className='bg-background border !border-[#484848] !text-white'
55+
size='xl'
56+
icon={
57+
<Image
58+
src='/auth/google.svg'
59+
alt='google'
60+
width={24}
61+
height={24}
62+
className='object-cover'
63+
/>
64+
}
65+
className='bg-background hover:!text-background border !border-[#484848] text-base !text-white'
5966
>
60-
<Image
61-
src='/auth/google.svg'
62-
alt='google'
63-
width={24}
64-
height={24}
65-
className='object-cover'
66-
unoptimized
67-
/>
6867
Continue with Google
6968
</BoundlessButton>
7069

7170
<div className='flex items-center justify-center gap-2.5'>
7271
<div className='h-[1px] w-full bg-[#2B2B2B]'></div>
73-
<p className='text-center text-sm text-[#B5B5B5]'>Or</p>
72+
<p className='text-center text-sm leading-[145%] text-[#B5B5B5]'>
73+
Or
74+
</p>
7475
<div className='h-[1px] w-full bg-[#2B2B2B]'></div>
7576
</div>
7677

@@ -160,19 +161,13 @@ const LoginForm = ({
160161
className='w-full'
161162
disabled={isLoading || !form.formState.isValid}
162163
fullWidth
164+
size='xl'
163165
loading={isLoading}
164166
>
165167
Sign in
166168
</BoundlessButton>
167169
</form>
168170
</Form>
169-
170-
<p className='text-center text-xs text-[#D9D9D9] lg:text-sm'>
171-
Don&apos;t have an account?{' '}
172-
<Link href='/auth/signup' className='text-primary underline'>
173-
Sign up
174-
</Link>
175-
</p>
176171
</div>
177172
</>
178173
);

0 commit comments

Comments
 (0)