-
Login
+
+
-
diff --git a/pages/logout/index.tsx b/pages/logout/index.tsx
new file mode 100644
index 0000000..0cf2d44
--- /dev/null
+++ b/pages/logout/index.tsx
@@ -0,0 +1,16 @@
+import { NextPage } from 'next';
+import { useRouter } from 'next/router';
+import { useEffect } from 'react';
+
+const LogoutPage: NextPage = () => {
+ const router = useRouter();
+
+ useEffect(() => {
+ document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
+ router.reload();
+ router.push('/');
+ }, [router]);
+ return
;
+};
+
+export default LogoutPage;
diff --git a/pages/register/index.tsx b/pages/register/index.tsx
new file mode 100644
index 0000000..c65ba97
--- /dev/null
+++ b/pages/register/index.tsx
@@ -0,0 +1,165 @@
+import ErrorAlert from '@/components/ui/alerts/ErrorAlert';
+import Button from '@/components/ui/forms/Button';
+import FormError from '@/components/ui/forms/FormError';
+import FormInfo from '@/components/ui/forms/FormInfo';
+import FormLabel from '@/components/ui/forms/FormLabel';
+import FormSegment from '@/components/ui/forms/FormSegment';
+import FormTextInput from '@/components/ui/forms/FormTextInput';
+import Layout from '@/components/ui/Layout';
+import sendRegisterUserRequest from '@/requests/sendRegisterUserRequest';
+import CreateUserValidationSchema from '@/services/User/schema/CreateUserValidationSchema';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { NextPage } from 'next';
+import { useState } from 'react';
+import { useForm } from 'react-hook-form';
+import { FaUserCircle } from 'react-icons/fa';
+import { z } from 'zod';
+
+interface RegisterUserProps {}
+
+const RegisterUserPage: NextPage
= () => {
+ const { reset, register, handleSubmit, formState } = useForm<
+ z.infer
+ >({
+ resolver: zodResolver(CreateUserValidationSchema),
+ });
+
+ const { errors } = formState;
+
+ const [serverResponseError, setServerResponseError] = useState('');
+
+ const onSubmit = async (data: z.infer) => {
+ try {
+ await sendRegisterUserRequest(data);
+ reset();
+ } catch (error) {
+ setServerResponseError(
+ error instanceof Error
+ ? error.message
+ : 'Something went wrong. We could not register your account.',
+ );
+ }
+ };
+
+ return (
+
+
+
+ );
+};
+
+export default RegisterUserPage;
diff --git a/prisma/seed/create/createNewUsers.ts b/prisma/seed/create/createNewUsers.ts
index fc189f4..9af1101 100644
--- a/prisma/seed/create/createNewUsers.ts
+++ b/prisma/seed/create/createNewUsers.ts
@@ -18,7 +18,7 @@ const createNewUsers = async ({ numberOfUsers }: CreateNewUsersArgs) => {
// eslint-disable-next-line no-plusplus
for (let i = 0; i < numberOfUsers; i++) {
- const randomValue = crypto.randomBytes(2).toString('hex');
+ const randomValue = crypto.randomBytes(4).toString('hex');
const firstName = faker.name.firstName();
const lastName = faker.name.lastName();
const username = `${firstName[0]}.${lastName}.${randomValue}`;
diff --git a/requests/sendLoginUserRequest.ts b/requests/sendLoginUserRequest.ts
index f664c79..7be9e64 100644
--- a/requests/sendLoginUserRequest.ts
+++ b/requests/sendLoginUserRequest.ts
@@ -17,6 +17,9 @@ const sendLoginUserRequest = async (data: { username: string; password: string }
throw new Error('API response validation failed');
}
+ if (!parsed.data.success) {
+ throw new Error(parsed.data.message);
+ }
const parsedPayload = BasicUserInfoSchema.safeParse(parsed.data.payload);
if (!parsedPayload.success) {
throw new Error('API response payload validation failed');
diff --git a/requests/sendRegisterUserRequest.ts b/requests/sendRegisterUserRequest.ts
new file mode 100644
index 0000000..7e106b4
--- /dev/null
+++ b/requests/sendRegisterUserRequest.ts
@@ -0,0 +1,35 @@
+import CreateUserValidationSchema from '@/services/User/schema/CreateUserValidationSchema';
+import GetUserSchema from '@/services/User/schema/GetUserSchema';
+import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
+import { z } from 'zod';
+
+async function sendRegisterUserRequest(data: z.infer) {
+ const response = await fetch('/api/users/register', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ });
+
+ const json = await response.json();
+ const parsed = APIResponseValidationSchema.safeParse(json);
+
+ if (!parsed.success) {
+ throw new Error('API response validation failed.');
+ }
+
+ if (!parsed.data.success) {
+ throw new Error(parsed.data.message);
+ }
+
+ const parsedPayload = GetUserSchema.safeParse(parsed.data.payload);
+
+ if (!parsedPayload.success) {
+ throw new Error('API response payload validation failed.');
+ }
+
+ return parsedPayload.data;
+}
+
+export default sendRegisterUserRequest;
diff --git a/services/User/schema/CreateUserValidationSchema.ts b/services/User/schema/CreateUserValidationSchema.ts
index e576656..850f815 100644
--- a/services/User/schema/CreateUserValidationSchema.ts
+++ b/services/User/schema/CreateUserValidationSchema.ts
@@ -2,36 +2,52 @@ import sub from 'date-fns/sub';
import { z } from 'zod';
const minimumDateOfBirth = sub(new Date(), { years: 19 });
-const CreateUserValidationSchema = z.object({
- email: z.string().email({ message: 'Email must be a valid email address.' }),
- // use special characters, numbers, and uppercase letters
- password: z
- .string()
- .min(8, { message: 'Password must be at least 8 characters.' })
- .refine((password) => /[A-Z]/.test(password), {
- message: 'Password must contain at least one uppercase letter.',
- })
- .refine((password) => /[0-9]/.test(password), {
- message: 'Password must contain at least one number.',
- })
- .refine((password) => /[^a-zA-Z0-9]/.test(password), {
- message: 'Password must contain at least one special character.',
- }),
-
- firstName: z.string().min(1, { message: 'First name must not be empty.' }),
- lastName: z.string().min(1, { message: 'Last name must not be empty.' }),
- username: z
- .string()
- .min(1, { message: 'Username must not be empty.' })
- .max(20, { message: 'Username must be less than 20 characters.' }),
- dateOfBirth: z.string().refine(
- (dateOfBirth) => {
- const parsedDateOfBirth = new Date(dateOfBirth);
-
- return parsedDateOfBirth <= minimumDateOfBirth;
- },
- { message: 'You must be at least 19 years old to register.' },
- ),
-});
+const CreateUserValidationSchema = z
+ .object({
+ email: z.string().email({ message: 'Email must be a valid email address.' }),
+ // use special characters, numbers, and uppercase letters
+ password: z
+ .string()
+ .min(8, { message: 'Password must be at least 8 characters.' })
+ .refine((password) => /[A-Z]/.test(password), {
+ message: 'Password must contain at least one uppercase letter.',
+ })
+ .refine((password) => /[0-9]/.test(password), {
+ message: 'Password must contain at least one number.',
+ })
+ .refine((password) => /[^a-zA-Z0-9]/.test(password), {
+ message: 'Password must contain at least one special character.',
+ }),
+ confirmPassword: z.string(),
+ firstName: z
+ .string()
+ .min(1, { message: 'First name must not be empty.' })
+ .max(20, { message: 'First name must be less than 20 characters.' })
+ .refine((firstName) => /^[a-zA-Z]+$/.test(firstName), {
+ message: 'First name must only contain letters.',
+ }),
+ lastName: z
+ .string()
+ .min(1, { message: 'Last name must not be empty.' })
+ .max(20, { message: 'Last name must be less than 20 characters.' })
+ .refine((lastName) => /^[a-zA-Z]+$/.test(lastName), {
+ message: 'Last name must only contain letters.',
+ }),
+ username: z
+ .string()
+ .min(1, { message: 'Username must not be empty.' })
+ .max(20, { message: 'Username must be less than 20 characters.' }),
+ dateOfBirth: z.string().refine(
+ (dateOfBirth) => {
+ const parsedDateOfBirth = new Date(dateOfBirth);
+ return parsedDateOfBirth <= minimumDateOfBirth;
+ },
+ { message: 'You must be at least 19 years old to register.' },
+ ),
+ })
+ .refine((data) => data.password === data.confirmPassword, {
+ message: 'Passwords do not match.',
+ path: ['confirmPassword'],
+ });
export default CreateUserValidationSchema;
diff --git a/services/User/schema/GetUserSchema.ts b/services/User/schema/GetUserSchema.ts
index 529f824..2619334 100644
--- a/services/User/schema/GetUserSchema.ts
+++ b/services/User/schema/GetUserSchema.ts
@@ -3,12 +3,12 @@ import { z } from 'zod';
const GetUserSchema = z.object({
id: z.string().uuid(),
username: z.string(),
- createdAt: z.date().or(z.string()),
- updatedAt: z.date().or(z.string()).optional(),
+ createdAt: z.coerce.date(),
+ updatedAt: z.coerce.date().optional(),
email: z.string().email(),
firstName: z.string(),
lastName: z.string(),
- dateOfBirth: z.date().or(z.string()),
+ dateOfBirth: z.coerce.date(),
});
export default GetUserSchema;