mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-28 17:10:58 +00:00
154 lines
4.3 KiB
TypeScript
154 lines
4.3 KiB
TypeScript
import {
|
|
Paper,
|
|
TextInput,
|
|
Button,
|
|
Stack,
|
|
Title,
|
|
Text,
|
|
Anchor,
|
|
} from '@mantine/core';
|
|
import { useForm } from 'react-hook-form';
|
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
import { z } from 'zod';
|
|
import { useNavigate, Link } from 'react-router-dom';
|
|
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
|
|
import { signupStart, signupSuccess, signupFailure } from '../store/signup.slice';
|
|
import { hydrateAuth } from '../store/auth.slice';
|
|
import { authStorage } from '../utils/auth-storage';
|
|
import { notify } from '@ema-platform/ui';
|
|
|
|
const BASE_API_URL =
|
|
(import.meta as { env?: Record<string, string> }).env?.['VITE_BASE_API_URL'] ??
|
|
'http://localhost:3001/api';
|
|
|
|
const schema = z.object({
|
|
email: z.string().email('Enter a valid email'),
|
|
username: z.string().min(3, 'Username must be at least 3 characters'),
|
|
phoneNumber: z.string().min(1, 'Phone number is required'),
|
|
userType: z.string().min(1, 'User type is required'),
|
|
name: z.string().min(1, 'Name is required'),
|
|
});
|
|
|
|
type FormValues = z.infer<typeof schema>;
|
|
|
|
interface SignupPayload {
|
|
email: string;
|
|
username: string;
|
|
phoneNumber: string;
|
|
userType: string;
|
|
name: {
|
|
am: string;
|
|
en: string;
|
|
};
|
|
}
|
|
|
|
export function SignupPage() {
|
|
const navigate = useNavigate();
|
|
const dispatch = useAppDispatch();
|
|
const { loading } = useAppSelector((s) => s.signup);
|
|
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
formState: { errors },
|
|
} = useForm<FormValues>({
|
|
resolver: zodResolver(schema),
|
|
});
|
|
|
|
const onSubmit = async (values: FormValues) => {
|
|
dispatch(signupStart());
|
|
try {
|
|
const payload: SignupPayload = {
|
|
email: values.email,
|
|
username: values.username,
|
|
phoneNumber: values.phoneNumber,
|
|
userType: values.userType,
|
|
name: {
|
|
am: '',
|
|
en: values.name,
|
|
},
|
|
};
|
|
|
|
const res = await fetch(`${BASE_API_URL}/auth/signup`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const errorData = await res.json().catch(() => null);
|
|
throw new Error(errorData?.message ?? 'Signup failed');
|
|
}
|
|
|
|
const data = (await res.json()) as { token: string; refreshToken: string };
|
|
|
|
authStorage.setToken(data.token);
|
|
authStorage.setRefreshToken(data.refreshToken);
|
|
dispatch(hydrateAuth());
|
|
dispatch(signupSuccess());
|
|
|
|
navigate('/set-password', { state: { email: values.email } });
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'Something went wrong';
|
|
dispatch(signupFailure(msg));
|
|
notify.error(msg);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Paper p="xl" shadow="md" radius="md">
|
|
<Stack gap="lg">
|
|
<Stack gap={4}>
|
|
<Title order={2}>Create an account</Title>
|
|
<Text c="dimmed" size="sm">
|
|
Fill in your details to get started
|
|
</Text>
|
|
</Stack>
|
|
<form onSubmit={handleSubmit(onSubmit)}>
|
|
<Stack gap="md">
|
|
<TextInput
|
|
label="Full Name"
|
|
placeholder="Your full name"
|
|
error={errors.name?.message}
|
|
{...register('name')}
|
|
/>
|
|
<TextInput
|
|
label="Email"
|
|
placeholder="you@example.com"
|
|
error={errors.email?.message}
|
|
{...register('email')}
|
|
/>
|
|
<TextInput
|
|
label="Username"
|
|
placeholder="Choose a username"
|
|
error={errors.username?.message}
|
|
{...register('username')}
|
|
/>
|
|
<TextInput
|
|
label="Phone Number"
|
|
placeholder="+251 911 234 567"
|
|
error={errors.phoneNumber?.message}
|
|
{...register('phoneNumber')}
|
|
/>
|
|
<TextInput
|
|
label="User Type"
|
|
placeholder="e.g. admin, manager"
|
|
error={errors.userType?.message}
|
|
{...register('userType')}
|
|
/>
|
|
<Button type="submit" loading={loading} fullWidth mt="sm">
|
|
Sign up
|
|
</Button>
|
|
</Stack>
|
|
</form>
|
|
<Text ta="center" size="sm" c="dimmed">
|
|
Already have an account?{' '}
|
|
<Anchor component={Link} to="/login">
|
|
Sign in
|
|
</Anchor>
|
|
</Text>
|
|
</Stack>
|
|
</Paper>
|
|
);
|
|
}
|