Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 102 additions & 50 deletions src/components/auth/signup.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,83 @@
'use client';
import React from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import APP_PATHS from '@/config/path.config';
import {
SignupSchema,
SignupSchemaType,
} from '@/lib/validators/auth.validator';
import { zodResolver } from '@hookform/resolvers/zod';

import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useForm } from 'react-hook-form';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '../ui/form';
} from '@/components/ui/form';
import { Check, X } from 'lucide-react';
import Link from 'next/link';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import {
SignupSchemaType,
SignupSchema,
} from '@/lib/validators/auth.validator';
import { PasswordInput } from '../password-input';
import { DemarcationLine, GoogleOauthButton } from './social-auth';
import { useToast } from '../ui/use-toast';
import APP_PATHS from '@/config/path.config';
import { useRouter } from 'next/navigation';
import { signUp } from '@/actions/auth.actions';
import { DemarcationLine, GoogleOauthButton } from './social-auth';
import { PasswordInput } from '../password-input';

interface InputProps {
fulfilled: boolean;
text: string;
}

const PasswordRequirement = ({ fulfilled, text }: InputProps) => (
<div className="flex items-center gap-2 text-sm">
{fulfilled ? (
<Check className="h-4 w-4 text-green-500" />
) : (
<X className="h-4 w-4 text-red-500" />
)}
<span className={fulfilled ? 'text-green-700' : 'text-red-700'}>
{text}
</span>
</div>
);

export const Signup = () => {
const { toast } = useToast();
const router = useRouter();

const form = useForm<SignupSchemaType>({
resolver: zodResolver(SignupSchema),
mode: 'onChange',
defaultValues: {
name: '',
email: '',
password: '',
},
});

async function signupHandler(data: SignupSchemaType) {
const watchPassword = form.watch('password');

const passwordRequirements = [
{
fulfilled: /[a-z]/.test(watchPassword),
text: 'Contains lowercase letter',
},
{
fulfilled: /[A-Z]/.test(watchPassword),
text: 'Contains uppercase letter',
},
{
fulfilled: /[0-9]/.test(watchPassword),
text: 'Contains number',
},
{
fulfilled: /[!@#$%^&*(),.?":{}|<>]/.test(watchPassword),
text: 'Contains special character',
},
];

const handleSubmit = async (data: SignupSchemaType) => {
try {
const response = await signUp(data);
if (!response.status) {
Expand All @@ -47,27 +87,23 @@ export const Signup = () => {
});
} else {
toast({
title: response.message || 'Signup successful! Welcome to 100xJobs!',
title: response.message || 'Signup successful! Welcome!',
variant: 'success',
});

router.push(APP_PATHS.WELCOME);
}
} catch {
toast({
title: 'something went wrong',
title: 'Something went wrong',
variant: 'destructive',
});
}
}
};

return (
<>
<div className="w-full max-w-md mx-auto space-y-6">
<Form {...form}>
<form
onSubmit={form.handleSubmit(signupHandler)}
className="w-full space-y-6"
>
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6">
<FormField
control={form.control}
name="name"
Expand All @@ -81,63 +117,79 @@ export const Signup = () => {
</FormItem>
)}
/>

<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email address</FormLabel>
<FormControl>
<Input {...field} placeholder="[email protected]" />
<Input
{...field}
type="email"
placeholder="[email protected]"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<PasswordInput field={field} placeholder="Password" />
<PasswordInput
field={field}
placeholder="Enter your password"
/>
</FormControl>
<FormMessage />
<div className="mt-2 space-y-1 flex justify-between">
<div>
{passwordRequirements.map((req, idx) => (
<PasswordRequirement key={idx} {...req} />
))}
</div>
<div>
<Link
href={APP_PATHS.FORGOT_PASSWORD}
className="text-xs text-muted-foreground font-medium hover:underline "
>
Forgot your password?
</Link>
</div>
</div>
</FormItem>
)}
/>
<div className="flex justify-end">
<Link
href={APP_PATHS.FORGOT_PASSWORD}
className="text-xs text-muted-foreground font-medium hover:underline"
>
Forgot your password?
</Link>
</div>

<Button
type="submit"
disabled={form.formState.isSubmitting}
className="w-full h-10"
aria-label="submit"
className="w-full"
disabled={form.formState.isSubmitting || !form.formState.isValid}
>
{form.formState.isSubmitting ? 'Please wait...' : 'Create Account'}
{form.formState.isSubmitting
? 'Creating Account...'
: 'Create Account'}
</Button>

<DemarcationLine />
<GoogleOauthButton label="Sign up with Google" />
</form>
</Form>
<div className="flex items-center justify-center mt-6">
<span className="text-muted-foreground">
Already have an account?{' '}
<Link
href={APP_PATHS.SIGNIN}
className="text-muted-foreground font-semibold hover:underline"
>
Sign In
</Link>
</span>

<div className="text-center text-sm text-gray-600">
Already have an account?{' '}
<Link href="/signin" className="font-semibold hover:underline">
Sign in
</Link>
</div>
</>
</div>
);
};

export default Signup;
11 changes: 10 additions & 1 deletion src/lib/validators/auth.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@ export type SigninSchemaType = z.infer<typeof SigninSchema>;
export const SignupSchema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Email is invalid').min(1, 'Email is required'),
password: z.string().min(1, 'Password is required'),
password: z
.string()
.min(1, 'Password is required')
.regex(/[a-z]/, 'Password must contain one lowercase letter')
.regex(/[A-Z]/, 'Password must contain one uppercase letter')
.regex(/[0-9]/, 'Password must contain atleast one number')
.regex(
/[!@#$%^&*(),.?":{}|<>]/,
'Password must contain at least one special character'
),
});

export type SignupSchemaType = z.infer<typeof SignupSchema>;