import { useState } from 'react'; import { Alert, Anchor, Button, Checkbox, Group, PasswordInput, SimpleGrid, Stack, Text, TextInput, Title, } from '@mantine/core'; import { IconArrowRight, IconAt, IconDeviceMobile, IconLock, IconMail, IconUser, } from '@tabler/icons-react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { useNavigate, Link } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { useApiMutation } from '@ema-platform/api'; import { notify } from '@ema-platform/ui'; import { AuthShell } from '../components/AuthShell'; import { loginSuccess, setUser } from '../store/auth.slice'; import type { AuthUser } from '../types/auth.types'; import { useAuthConfig } from '../AuthConfig'; const schema = z .object({ email: z.string().email(), username: z.string().min(3, { message: 'Username must be at least 3 characters' }), phoneNumber: z.string().min(1, { message: 'Phone number is required' }), userType: z.literal('individual'), nameEn: z.string().min(1, { message: 'Name (English) is required' }), nameAm: z.string().optional(), password: z.string().min(8, { message: 'Password must be at least 8 characters' }), confirmPassword: z.string().min(8, { message: 'Confirm your password' }), }) .refine((data) => data.password === data.confirmPassword, { message: 'Passwords do not match', path: ['confirmPassword'], }); type FormValues = z.infer; interface SignupPayload { email: string; username: string; phoneNumber: string; userType: string; name: { am: string; en: string; }; password: string; confirmPassword: string; } export function SignupPage() { const navigate = useNavigate(); const dispatch = useDispatch(); const { appName, loginRedirectPath } = useAuthConfig(); const [agreed, setAgreed] = useState(false); const [serverError, setServerError] = useState(null); const [signupTrigger, { isLoading: loading }] = useApiMutation<{ token: string; refreshToken: string; isPhoneNumberVerified: boolean; }>(); const [meTrigger] = useApiMutation(); const { register, handleSubmit, formState: { errors }, } = useForm({ resolver: zodResolver(schema), defaultValues: { userType: 'individual' }, }); const onSubmit = async (values: FormValues) => { try { const payload: SignupPayload = { email: values.email, username: values.username, phoneNumber: values.phoneNumber, userType: values.userType, name: { en: values.nameEn, am: values.nameAm ?? '' }, password: values.password, confirmPassword: values.confirmPassword, }; const data = await signupTrigger({ url: '/auth/signup-with-pwd', method: 'POST', body: payload, }).unwrap(); dispatch( loginSuccess({ token: data.token, refreshToken: data.refreshToken, isPhoneNumberVerified: data.isPhoneNumberVerified, }), ); const me = await meTrigger({ url: '/auth/me', method: 'GET' }).unwrap(); dispatch(setUser(me)); if (data.isPhoneNumberVerified) { navigate(loginRedirectPath); } else { navigate('/otp-verify', { state: { email: values.email, phoneNumber: values.phoneNumber, }, }); } } catch (err: unknown) { const msg = (err as { data?: { message?: string } })?.data?.message ?? (err instanceof Error ? err.message : 'Something went wrong'); setServerError(msg); notify.error(msg); } }; return (
Create account It only takes a minute to get started.
{serverError && ( setServerError(null)}> {serverError} )}
} error={errors.nameEn?.message} {...register('nameEn')} /> } error={errors.nameAm?.message} {...register('nameAm')} /> } error={errors.email?.message} {...register('email')} /> } error={errors.username?.message} {...register('username')} /> } error={errors.phoneNumber?.message} {...register('phoneNumber')} /> } error={errors.password?.message} {...register('password')} /> } error={errors.confirmPassword?.message} {...register('confirmPassword')} /> setAgreed(e.currentTarget.checked)} label={ I agree to the{' '} e.preventDefault()} > Terms & Privacy Policy } />
Already have an account?{' '} Sign in
); }