Skip to content

Commit

Permalink
Merge pull request #20 from BinaryStudioAcademy/task/OV-4-add-sign-up…
Browse files Browse the repository at this point in the history
…-flow

OV-4: Sign Up flow
  • Loading branch information
nikita-remeslov authored Aug 23, 2024
2 parents 009d04a + fd90ba7 commit ce3c455
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 17 deletions.
2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"scripts": {
"lint:type": "npx tsc --noEmit",
"lint:js": "npx eslint \"src/**/*.ts\"",
"lint:js:fix": "npx eslint --fix \"src/**/*.ts\"",
"lint": "npm run lint:type && npm run lint:js",
"lint:fix": "npm run lint:type && npm run lint:js:fix",
"start:dev": "nodemon --exec tsx src/index.ts",
"migrate:dev": "node --loader ts-paths-esm-loader ../node_modules/knex/bin/cli.js migrate:latest",
"migrate:dev:make": "node --loader ts-paths-esm-loader ../node_modules/knex/bin/cli.js migrate:make -x ts",
Expand Down
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
"scripts": {
"lint:css": "npx stylelint \"src/**/*.scss\" --aei",
"lint:js": "npx eslint \"src/**/*.{ts,tsx}\"",
"lint:js:fix": "npx eslint --fix \"src/**/*.{ts,tsx}\"",
"lint:type": "npx tsc --noEmit",
"lint": "npm run lint:type && npm run lint:js",
"lint:fix": "npm run lint:type && npm run lint:js:fix",
"start:dev": "vite",
"build": "tsc -p tsconfig.build.json && vite build",
"preview": "vite preview"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { type UserSignUpRequestDto } from '~/bundles/users/users.js';

const DEFAULT_SIGN_UP_PAYLOAD: UserSignUpRequestDto = {
name: '',
email: '',
password: '',
confirmPassword: '',
};

export { DEFAULT_SIGN_UP_PAYLOAD };
69 changes: 58 additions & 11 deletions frontend/src/bundles/auth/components/sign-up-form/sign-up-form.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,99 @@
import { UserValidationMessage } from 'shared/src/bundles/users/users.js';

import {
Box,
Button,
FormProvider,
Heading,
Input,
Link,
VStack,
} from '~/bundles/common/components/components.js';
import { useAppForm } from '~/bundles/common/hooks/hooks.js';
import { AppRoute, DataStatus } from '~/bundles/common/enums/enums.js';
import {
useAppForm,
useAppSelector,
useMemo,
} from '~/bundles/common/hooks/hooks.js';
import {
type UserSignUpRequestDto,
userSignUpValidationSchema,
} from '~/bundles/users/users.js';

import { FormError, FormHeader, PasswordInput } from '../common/components.js';
import { DEFAULT_SIGN_UP_PAYLOAD } from './constants/constants.js';

type Properties = {
onSubmit: (payload: UserSignUpRequestDto) => void;
};

const SignUpForm: React.FC<Properties> = ({ onSubmit }) => {
const { dataStatus } = useAppSelector(({ auth }) => ({
dataStatus: auth.dataStatus,
}));
const form = useAppForm<UserSignUpRequestDto>({
initialValues: DEFAULT_SIGN_UP_PAYLOAD,
validationSchema: userSignUpValidationSchema,
onSubmit,
});

const { handleSubmit } = form;
const { handleSubmit, errors, values } = form;

const isEmpty = useMemo(
() => Object.values(values).some((value) => value.trim().length === 0),
[values],
);

return (
<FormProvider value={form}>
<Box bg="brand.200" w={64} p={6} rounded="md">
<Heading as="h1">Sign Up</Heading>

<Box w="55%" color="white">
<FormHeader
headerText="Create an account"
subheader={
<>
Already registerd?{' '}
<Link to={AppRoute.SIGN_IN} variant="secondary">
Log In
</Link>
</>
}
/>
<form onSubmit={handleSubmit}>
<VStack spacing={4} align="flex-start">
<Input
type="text"
label="Full Name"
placeholder="Name"
name="name"
/>
<Input
type="email"
label="Email"
placeholder="Enter your email"
placeholder="[email protected]"
name="email"
/>
<Input
type="password"
<PasswordInput
label="Password"
placeholder="Enter your password"
name="password"
hasError={Boolean(errors.password)}
/>
<PasswordInput
label="Repeat password"
name="confirmPassword"
hasError={Boolean(errors.confirmPassword)}
/>
<FormError
isVisible={dataStatus === DataStatus.REJECTED}
message={
UserValidationMessage.USER_IS_NOT_AVAILABLE
}
/>
<Button
type="submit"
label="Sign up"
size="lg"
sx={{ mt: '16px' }}
isDisabled={isEmpty}
/>
<Button type="submit" label="Sign up" />
</VStack>
</form>
</Box>
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/bundles/auth/store/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,15 @@ const { reducer, actions, name } = createSlice({
builder.addCase(signUp.pending, (state) => {
state.dataStatus = DataStatus.PENDING;
});
builder.addCase(signUp.fulfilled, (state) => {
builder.addCase(signUp.fulfilled, (state, action) => {
const payload = action.payload;

state.dataStatus = DataStatus.FULFILLED;
state.user = payload;
});
builder.addCase(signUp.rejected, (state) => {
state.dataStatus = DataStatus.REJECTED;
state.user = null;
});
},
});
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
"lint:fs": "ls-lint",
"lint:type": "npm run lint:type --workspaces --if-present",
"lint:js": "npm run lint:js --workspaces --if-present -- --max-warnings=0",
"lint:js:fix": "eslint --fix \"**/*.{ts,tsx}\" --max-warnings=0",
"lint:css": "npm run lint:css --workspaces --if-present",
"lint:css:fix": "stylelint --fix \"**/*.{scss,css}\"",
"lint:format": "prettier --check \"**/*.{ts,tsx,json,md,scss,html,yml}\"",
"lint": "npm run lint:editor && npm run lint:fs && npm run lint:format && npm run lint:type && npm run lint:js && npm run lint:css",
"lint:fix": "npm run lint:fix -w frontend && npm run lint:fix -w backend",
"format": "prettier --write \"**/*.{ts,tsx,json,md,css,html,yml}\""
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ const UserValidationMessage = {
FIELD_REQUIRE: 'Please fill out this field',
EMAIL_INVALID: 'Please enter a valid email',
PASSWORD_LENGTH: 'Password must have from 6 to 12 characters',
PASS_DONT_MATCH: 'Passwords must be identical',
INVALID_DATA: 'Incorrect email or password. Please try again.',
WRONG_CREDENTIALS: 'Email or password are incorrect',
USER_IS_NOT_AVAILABLE:
'User with this email already exists. Log in if it is you',
} as const;

export { UserValidationMessage };
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
type UserSignUpRequestDto = {
name: string;
email: string;
password: string;
confirmPassword: string;
};

export { type UserSignUpRequestDto };
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,52 @@ import { z } from 'zod';
import { UserValidationMessage, UserValidationRule } from '../enums/enums.js';

type UserSignUpRequestValidationDto = {
name: z.ZodString;
email: z.ZodString;
password: z.ZodString;
confirmPassword: z.ZodString;
};

const userSignUp = z
.object<UserSignUpRequestValidationDto>({
name: z
.string({ required_error: UserValidationMessage.FIELD_REQUIRE })
.trim(),
email: z
.string()
.string({ required_error: UserValidationMessage.FIELD_REQUIRE })
.trim()
.min(UserValidationRule.EMAIL_MINIMUM_LENGTH, {
message: UserValidationMessage.EMAIL_REQUIRE,
message: UserValidationMessage.EMAIL_INVALID,
})
.max(UserValidationRule.EMAIL_MAXIMUM_LENGTH, {
message: UserValidationMessage.EMAIL_INVALID,
})
.email({
message: UserValidationMessage.EMAIL_WRONG,
message: UserValidationMessage.EMAIL_INVALID,
}),
password: z
.string({ required_error: UserValidationMessage.FIELD_REQUIRE })
.trim()
.min(UserValidationRule.PASSWORD_MINIMUM_LENGTH, {
message: UserValidationMessage.PASSWORD_LENGTH,
})
.max(UserValidationRule.PASSWORD_MAXIMUM_LENGTH, {
message: UserValidationMessage.PASSWORD_LENGTH,
}),
confirmPassword: z
.string({ required_error: UserValidationMessage.FIELD_REQUIRE })
.trim()
.min(UserValidationRule.PASSWORD_MINIMUM_LENGTH, {
message: UserValidationMessage.PASSWORD_LENGTH,
})
.max(UserValidationRule.PASSWORD_MAXIMUM_LENGTH, {
message: UserValidationMessage.PASSWORD_LENGTH,
}),
password: z.string().trim(),
})
.required();
.required()
.refine((data) => data.password === data.confirmPassword, {
message: UserValidationMessage.PASS_DONT_MATCH,
path: ['confirmPassword'],
});

export { userSignUp };

0 comments on commit ce3c455

Please sign in to comment.