Skip to content

Commit

Permalink
Merge pull request #33 from 0xdevcollins/feat/dashboard
Browse files Browse the repository at this point in the history
feat(auth): add email verification and OTP resend functionality
  • Loading branch information
0xdevcollins authored Dec 3, 2024
2 parents a99eea6 + 73d074e commit 10b1194
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 18 deletions.
6 changes: 6 additions & 0 deletions app/api/auth/register/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { handleApiRequest } from '@/lib/api-utils'
import { NextRequest } from 'next/server'

export async function POST(request: NextRequest) {
return handleApiRequest(request, '/auth/register', 'POST')
}
6 changes: 6 additions & 0 deletions app/api/auth/resend/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { handleApiRequest } from '@/lib/api-utils'
import { NextRequest } from 'next/server'

export async function POST(request: NextRequest) {
return handleApiRequest(request, 'auth/resend-verification-mail', 'POST')
}
6 changes: 6 additions & 0 deletions app/api/auth/verify/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { handleApiRequest } from '@/lib/api-utils'
import { NextRequest } from 'next/server'

export async function POST(request: NextRequest) {
return handleApiRequest(request, '/auth/email/verify', 'POST')
}
10 changes: 6 additions & 4 deletions app/auth/otp/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { useRouter, useSearchParams } from 'next/navigation';
import { motion } from 'framer-motion';
import { Button } from '@/components/ui/button';
import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/ui/input-otp';
import { apiService } from '@/lib/api-service';
import { useToast } from '@/hooks/use-toast';
import { AuthCard } from '../auth-card';
import { REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp';
import { useAuth } from '@/hooks/useAuth';

export default function OtpPage() {
const router = useRouter();
Expand All @@ -18,17 +18,19 @@ export default function OtpPage() {
const [loading, setLoading] = useState(false);
const [otp, setOtp] = useState('');

const auth = useAuth();

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);

try {
await apiService.verifyEmail({ email, token: otp });
await auth.verify(email, otp);
toast({
title: 'Success',
description: 'Email verified successfully',
});
router.push('/auth/basic-info');
router.push('/auth/login');
} catch (error: any) {
toast({
variant: 'destructive',
Expand All @@ -42,7 +44,7 @@ export default function OtpPage() {

const handleResendOtp = async () => {
try {
await apiService.resendOtp(email);
await auth.resend(email);
toast({
title: 'Success',
description: 'OTP resent successfully',
Expand Down
25 changes: 13 additions & 12 deletions app/auth/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { motion } from 'framer-motion';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { apiService } from '@/lib/api-service';
import { useToast } from '@/hooks/use-toast';
import { AuthCard } from '../auth-card';
import { useAuthStore } from '@/store/authStore';
import { useAuth } from '@/hooks/useAuth';

export default function RegisterPage() {
const router = useRouter();
const { toast } = useToast();
const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState({
email: '',
password: '',
Expand All @@ -23,6 +23,9 @@ export default function RegisterPage() {
last_name: '',
});

const { register, loading } = useAuthStore();
const auth = useAuth();

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();

Expand All @@ -35,16 +38,16 @@ export default function RegisterPage() {
return;
}

setLoading(true);
// setLoading(true);

try {
await apiService.register({
email: formData.email,
password: formData.password,
last_name: formData.last_name,
first_name: formData.first_name,
password_confirmation: formData.confirmPassword,
});
await auth.register(
formData.email,
formData.password,
formData.confirmPassword,
formData.first_name,
formData.last_name
);
toast({
title: 'Success',
description: 'Registration successful. Please verify your email.',
Expand All @@ -56,8 +59,6 @@ export default function RegisterPage() {
title: 'Error',
description: error instanceof Error ? error.message : 'Failed to register',
});
} finally {
setLoading(false);
}
};

Expand Down
14 changes: 12 additions & 2 deletions hooks/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useRouter } from 'next/navigation';
import { useAuthStore } from '../store/authStore';

export function useAuth() {
const { user, loading, login, logout, register, fetchCurrentUser } = useAuthStore();
const { user, loading, login, logout, register, fetchCurrentUser, verifyEmail, resendOtp } = useAuthStore();
const router = useRouter();

useEffect(() => {
Expand All @@ -25,6 +25,16 @@ export function useAuth() {
router.push('/dashboard'); // Redirect to the dashboard after login
};

const verify = async (email: string, token: string) => {
await verifyEmail(email, token);
router.push('/auth/login');
};

const resend = async (email: string) => {
await resendOtp(email);
router.push('/auth/otp');
};

const handleLogout = async () => {
await logout();
router.push('/auth/login'); // Redirect to login page after logout
Expand All @@ -41,5 +51,5 @@ export function useAuth() {
router.push('/dashboard'); // Redirect to the dashboard after registration
};

return { user, loading, login: handleLogin, logout: handleLogout, register: handleRegister };
return { user, loading, login: handleLogin, logout: handleLogout, register: handleRegister, verify, resend };
}
44 changes: 44 additions & 0 deletions store/authStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ interface AuthState {
loading: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => Promise<void>;
verifyEmail: (email: string, token: string) => Promise<void>;
resendOtp: (email: string) => Promise<void>;
register: (
email: string,
password: string,
Expand Down Expand Up @@ -50,6 +52,48 @@ export const useAuthStore = create<AuthState>((set) => ({
throw error;
}
},
verifyEmail: async (email: string, token: string) => {
try {
set({ loading: true });

const response = await fetch('/api/auth/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, token }),
});

if (response.ok) {
set({ loading: false });
} else {
throw new Error('Verification failed');
}
} catch (error) {
console.error('Verification error:', error);
set({ loading: false });
throw error;
}
},
resendOtp: async (email: string) => {
try {
set({ loading: true });

const response = await fetch('/api/auth/resend', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});

if (response.ok) {
set({ loading: false });
} else {
throw new Error('Verification failed');
}
} catch (error) {
console.error('Verification error:', error);
set({ loading: false });
throw error;
}
},

logout: async () => {
try {
Expand Down

0 comments on commit 10b1194

Please sign in to comment.