diff --git a/backend/src/bundles/auth/auth.controller.ts b/backend/src/bundles/auth/auth.controller.ts index 148002532..f25c3d2d5 100644 --- a/backend/src/bundles/auth/auth.controller.ts +++ b/backend/src/bundles/auth/auth.controller.ts @@ -55,9 +55,9 @@ class AuthController extends BaseController { /** * @swagger - * /auth/sign-up: + * /auth/sign-in: * post: - * description: Sign up user into the application + * description: Sign in user into the application * requestBody: * description: User auth data * required: true @@ -72,7 +72,7 @@ class AuthController extends BaseController { * password: * type: string * responses: - * 201: + * 200: * description: Successful operation * content: * application/json: @@ -82,7 +82,15 @@ class AuthController extends BaseController { * message: * type: object * $ref: '#/components/schemas/User' + * 400: + * description: Failed operation + * content: + * application/json: + * schema: + * type: object + * $ref: '#/components/schemas/Error' */ + private async signIn( options: ApiHandlerOptions<{ body: UserSignInRequestDto; @@ -94,6 +102,38 @@ class AuthController extends BaseController { }; } + /** + * @swagger + * /auth/sign-up: + * post: + * description: Sign up user into the application + * requestBody: + * description: User auth data + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * email: + * type: string + * format: email + * password: + * type: string + * responses: + * 201: + * description: Successful operation + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: object + * $ref: '#/components/schemas/User' + */ + + private async signUp( options: ApiHandlerOptions<{ body: UserSignUpRequestDto; diff --git a/backend/src/bundles/auth/auth.service.ts b/backend/src/bundles/auth/auth.service.ts index fdcac92fc..24566df74 100644 --- a/backend/src/bundles/auth/auth.service.ts +++ b/backend/src/bundles/auth/auth.service.ts @@ -8,8 +8,10 @@ import { type UserSignInResponseDto, } from '~/bundles/users/users.js'; import { HttpCode, HttpError } from '~/common/http/http.js'; + import { cryptService, tokenService } from '~/common/services/services.js'; + import { UserValidationMessage } from './enums/enums.js'; class AuthService { @@ -45,6 +47,7 @@ class AuthService { status: HttpCode.BAD_REQUEST, }); } + const id = user.toObject().id; const token = await tokenService.createToken(id); return { ...user.toObject(), token }; diff --git a/backend/src/common/server-application/base-server-app-api.ts b/backend/src/common/server-application/base-server-app-api.ts index 6630eaebc..e86461e11 100644 --- a/backend/src/common/server-application/base-server-app-api.ts +++ b/backend/src/common/server-application/base-server-app-api.ts @@ -38,11 +38,27 @@ class BaseServerAppApi implements ServerAppApi { definition: { openapi: '3.0.0', info: { - title: 'Hello World', + title: 'OutreachVids API documentation', version: `${this.version}.0.0`, }, + components: { + schemas: { + Error: { + type: 'object', + properties: { + errorType: { + type: 'string', + enum: ['COMMON', 'VALIDATION'], + }, + message: { + type: 'string', + }, + }, + }, + }, + }, }, - apis: [`src/packages/**/*.controller.${controllerExtension}`], + apis: [`src/bundles/**/*.controller.${controllerExtension}`], }); } } diff --git a/frontend/package.json b/frontend/package.json index 881769413..25983088a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,9 +27,12 @@ "vite": "5.4.0" }, "dependencies": { + "@chakra-ui/icons": "2.1.1", "@chakra-ui/react": "2.8.2", "@emotion/react": "11.13.0", "@emotion/styled": "11.13.0", + "@fortawesome/free-solid-svg-icons": "6.6.0", + "@fortawesome/react-fontawesome": "0.2.2", "@reduxjs/toolkit": "2.2.7", "formik": "2.4.6", "framer-motion": "11.3.24", diff --git a/frontend/src/app/app.tsx b/frontend/src/app/app.tsx index e1f671b35..54a2eff7f 100644 --- a/frontend/src/app/app.tsx +++ b/frontend/src/app/app.tsx @@ -1,61 +1,9 @@ -import reactLogo from '~/assets/img/react.svg'; -import { Link, RouterOutlet } from '~/bundles/common/components/components.js'; -import { AppRoute } from '~/bundles/common/enums/enums.js'; -import { - useAppDispatch, - useAppSelector, - useEffect, - useLocation, -} from '~/bundles/common/hooks/hooks.js'; -import { actions as userActions } from '~/bundles/users/store/users.js'; +import { RouterOutlet } from '~/bundles/common/components/components.js'; const App: React.FC = () => { - const { pathname } = useLocation(); - const dispatch = useAppDispatch(); - const { users, dataStatus } = useAppSelector(({ users }) => ({ - users: users.users, - dataStatus: users.dataStatus, - })); - - const isRoot = pathname === AppRoute.ROOT; - - useEffect(() => { - if (isRoot) { - void dispatch(userActions.loadAll()); - } - }, [isRoot, dispatch]); - return ( <> - logo - - -

Current path: {pathname}

- -
- -
- {isRoot && ( - <> -

Users:

-

Status: {dataStatus}

- - - )} + ); }; diff --git a/frontend/src/bundles/auth/auth-api.ts b/frontend/src/bundles/auth/auth-api.ts index a81074fa4..53c946f10 100644 --- a/frontend/src/bundles/auth/auth-api.ts +++ b/frontend/src/bundles/auth/auth-api.ts @@ -1,5 +1,7 @@ import { ApiPath, ContentType } from '~/bundles/common/enums/enums.js'; import { + type UserSignInRequestDto, + type UserSignInResponseDto, type UserSignUpRequestDto, type UserSignUpResponseDto, } from '~/bundles/users/users.js'; @@ -20,6 +22,22 @@ class AuthApi extends BaseHttpApi { super({ path: ApiPath.AUTH, baseUrl, http, storage }); } + public async signIn( + payload: UserSignInRequestDto, + ): Promise { + const response = await this.load( + this.getFullEndpoint(AuthApiPath.SIGN_IN, {}), + { + method: 'POST', + contentType: ContentType.JSON, + payload: JSON.stringify(payload), + hasAuth: false, + }, + ); + + return await response.json(); + } + public async signUp( payload: UserSignUpRequestDto, ): Promise { diff --git a/frontend/src/bundles/auth/components/common/components.ts b/frontend/src/bundles/auth/components/common/components.ts new file mode 100644 index 000000000..22726d9a9 --- /dev/null +++ b/frontend/src/bundles/auth/components/common/components.ts @@ -0,0 +1,3 @@ +export { FormError } from './form-error/form-error.js'; +export { FormHeader } from './form-header/form-header.js'; +export { PasswordInput } from './password-input/password-input.js'; diff --git a/frontend/src/bundles/auth/components/common/form-error/form-error.tsx b/frontend/src/bundles/auth/components/common/form-error/form-error.tsx new file mode 100644 index 000000000..965799129 --- /dev/null +++ b/frontend/src/bundles/auth/components/common/form-error/form-error.tsx @@ -0,0 +1,19 @@ +import { + FormControl, + FormErrorMessage, +} from '~/bundles/common/components/components.js'; + +type Properties = { + isVisible: boolean; + message: string; +}; + +const FormError: React.FC = ({ isVisible, message }) => { + return ( + + {message} + + ); +}; + +export { FormError }; diff --git a/frontend/src/bundles/auth/components/common/form-header/form-header.tsx b/frontend/src/bundles/auth/components/common/form-header/form-header.tsx new file mode 100644 index 000000000..47397c365 --- /dev/null +++ b/frontend/src/bundles/auth/components/common/form-header/form-header.tsx @@ -0,0 +1,23 @@ +import { Heading, Text } from '~/bundles/common/components/components.js'; + +type Properties = { + headerText: string; + subheader: React.ReactNode; +}; + +const FormHeader: React.FC = ({ headerText, subheader }) => { + return ( + <> + {/* TODO: Add logo */} +

LOGO

+ + {headerText} + + + {subheader} + + + ); +}; + +export { FormHeader }; diff --git a/frontend/src/bundles/auth/components/common/password-input/password-input.tsx b/frontend/src/bundles/auth/components/common/password-input/password-input.tsx new file mode 100644 index 000000000..57e8cf7ae --- /dev/null +++ b/frontend/src/bundles/auth/components/common/password-input/password-input.tsx @@ -0,0 +1,49 @@ +import { + IconButton, + Input, + InputGroup, + InputRightElement, + ViewIcon, + ViewOffIcon, +} from '~/bundles/common/components/components.js'; +import { useCallback, useState } from '~/bundles/common/hooks/hooks.js'; + +type Properties = { + label: string; + name: string; + hasError: boolean; +}; + +const PasswordInput: React.FC = ({ label, name, hasError }) => { + const [isPasswordVisible, setIsPasswordVisible] = useState(false); + + const handlePasswordIconClick = useCallback((): void => { + setIsPasswordVisible( + (previousIsPasswordVisible) => !previousIsPasswordVisible, + ); + }, []); + + return ( + + + + : } + onClick={handlePasswordIconClick} + variant="ghostIcon" + /> + + + ); +}; + +export { PasswordInput }; diff --git a/frontend/src/bundles/auth/components/sign-in-form/constants/constants.ts b/frontend/src/bundles/auth/components/sign-in-form/constants/constants.ts new file mode 100644 index 000000000..3e3264e83 --- /dev/null +++ b/frontend/src/bundles/auth/components/sign-in-form/constants/constants.ts @@ -0,0 +1,8 @@ +import { type UserSignInRequestDto } from '~/bundles/users/users.js'; + +const DEFAULT_SIGN_IN_PAYLOAD: UserSignInRequestDto = { + email: '', + password: '', +}; + +export { DEFAULT_SIGN_IN_PAYLOAD }; diff --git a/frontend/src/bundles/auth/components/sign-in-form/sign-in-form.tsx b/frontend/src/bundles/auth/components/sign-in-form/sign-in-form.tsx index bbcad3139..f3a73d1f5 100644 --- a/frontend/src/bundles/auth/components/sign-in-form/sign-in-form.tsx +++ b/frontend/src/bundles/auth/components/sign-in-form/sign-in-form.tsx @@ -1,17 +1,97 @@ -import { Button, Heading } from '~/bundles/common/components/components.js'; +import { + FormError, + FormHeader, + PasswordInput, +} from '~/bundles/auth/components/common/components.js'; +import { + Box, + Button, + FormProvider, + Input, + Link, + VStack, +} from '~/bundles/common/components/components.js'; +import { + AppRoute, + DataStatus, + UserValidationMessage, +} from '~/bundles/common/enums/enums.js'; +import { + useAppForm, + useAppSelector, + useMemo, +} from '~/bundles/common/hooks/hooks.js'; +import { + type UserSignInRequestDto, + userSignInValidationSchema, +} from '~/bundles/users/users.js'; + +import { DEFAULT_SIGN_IN_PAYLOAD } from './constants/constants.js'; type Properties = { - onSubmit: () => void; + onSubmit: (payload: UserSignInRequestDto) => void; }; -const SignInForm: React.FC = () => ( - <> - Sign In +const SignInForm: React.FC = ({ onSubmit }) => { + const { dataStatus } = useAppSelector(({ auth }) => ({ + dataStatus: auth.dataStatus, + })); + const form = useAppForm({ + initialValues: DEFAULT_SIGN_IN_PAYLOAD, + validationSchema: userSignInValidationSchema, + onSubmit, + }); + + const { handleSubmit, errors, values } = form; -
-
+ + + + ); +}; + +export { VideoPreview }; diff --git a/frontend/src/bundles/common/components/video-modal/components/video-modal-content/video-modal-content.tsx b/frontend/src/bundles/common/components/video-modal/components/video-modal-content/video-modal-content.tsx new file mode 100644 index 000000000..737d6bb2e --- /dev/null +++ b/frontend/src/bundles/common/components/video-modal/components/video-modal-content/video-modal-content.tsx @@ -0,0 +1,29 @@ +import { TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react'; +import { faPlay } from '@fortawesome/free-solid-svg-icons/faPlay'; + +import { Tab, VideoPreview } from './components/components.js'; + +const VideoModalContent = (): JSX.Element => { + return ( + + + + + + + + + + + ); +}; + +export { VideoModalContent }; diff --git a/frontend/src/bundles/common/components/video-modal/video-modal.tsx b/frontend/src/bundles/common/components/video-modal/video-modal.tsx new file mode 100644 index 000000000..dfcd36a13 --- /dev/null +++ b/frontend/src/bundles/common/components/video-modal/video-modal.tsx @@ -0,0 +1,44 @@ +import { + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalHeader, + ModalOverlay, +} from '@chakra-ui/react'; + +import { VideoModalContent } from './components/components.js'; + +type Properties = { + isOpen: boolean; + onModalClose: () => void; +}; + +const VideoModal = ({ isOpen, onModalClose }: Properties): JSX.Element => { + return ( + + + + + Create video + + + + + + + + ); +}; + +export { VideoModal }; diff --git a/frontend/src/bundles/common/enums/app-route.enum.ts b/frontend/src/bundles/common/enums/app-route.enum.ts index dfc5352ec..9d1c5334e 100644 --- a/frontend/src/bundles/common/enums/app-route.enum.ts +++ b/frontend/src/bundles/common/enums/app-route.enum.ts @@ -2,6 +2,7 @@ const AppRoute = { ROOT: '/', SIGN_IN: '/sign-in', SIGN_UP: '/sign-up', + STUDIO: '/studio', } as const; export { AppRoute }; diff --git a/frontend/src/bundles/common/enums/enums.ts b/frontend/src/bundles/common/enums/enums.ts index 0e65d3c5b..ef77ebca3 100644 --- a/frontend/src/bundles/common/enums/enums.ts +++ b/frontend/src/bundles/common/enums/enums.ts @@ -1,3 +1,9 @@ export { AppRoute } from './app-route.enum.js'; export { DataStatus } from './data-status.enum.js'; -export { ApiPath, AppEnvironment, ContentType, ServerErrorType } from 'shared'; +export { + ApiPath, + AppEnvironment, + ContentType, + ServerErrorType, + UserValidationMessage, +} from 'shared'; diff --git a/frontend/src/bundles/studio/pages/studio.tsx b/frontend/src/bundles/studio/pages/studio.tsx new file mode 100644 index 000000000..fd0acd3d0 --- /dev/null +++ b/frontend/src/bundles/studio/pages/studio.tsx @@ -0,0 +1,31 @@ +import { + Button, + DownloadIcon, + Header, + IconButton, +} from '~/bundles/common/components/components.js'; + +const Studio: React.FC = () => { + return ( + <> +
+ } + right={ + } + /> + } + /> + + ); +}; + +export { Studio }; diff --git a/frontend/src/bundles/users/types/types.ts b/frontend/src/bundles/users/types/types.ts index fb8e20c76..66268a76e 100644 --- a/frontend/src/bundles/users/types/types.ts +++ b/frontend/src/bundles/users/types/types.ts @@ -1,6 +1,8 @@ export { type UserGetAllItemResponseDto, type UserGetAllResponseDto, + type UserSignInRequestDto, + type UserSignInResponseDto, type UserSignUpRequestDto, type UserSignUpResponseDto, } from 'shared'; diff --git a/frontend/src/bundles/users/users.ts b/frontend/src/bundles/users/users.ts index 443083e1c..1c0f3bf9d 100644 --- a/frontend/src/bundles/users/users.ts +++ b/frontend/src/bundles/users/users.ts @@ -14,7 +14,12 @@ export { userApi }; export { type UserGetAllItemResponseDto, type UserGetAllResponseDto, + type UserSignInRequestDto, + type UserSignInResponseDto, type UserSignUpRequestDto, type UserSignUpResponseDto, } from './types/types.js'; -export { userSignUpValidationSchema } from './validation-schemas/validation-schemas.js'; +export { + userSignInValidationSchema, + userSignUpValidationSchema, +} from './validation-schemas/validation-schemas.js'; diff --git a/frontend/src/bundles/users/validation-schemas/validation-schemas.ts b/frontend/src/bundles/users/validation-schemas/validation-schemas.ts index 7bc9a09c5..5952fe0cf 100644 --- a/frontend/src/bundles/users/validation-schemas/validation-schemas.ts +++ b/frontend/src/bundles/users/validation-schemas/validation-schemas.ts @@ -1 +1 @@ -export { userSignUpValidationSchema } from 'shared'; +export { userSignInValidationSchema, userSignUpValidationSchema } from 'shared'; diff --git a/frontend/src/framework/theme/styles/colors.styles.ts b/frontend/src/framework/theme/styles/colors.styles.ts index 10ec09c65..8ae8d0cce 100644 --- a/frontend/src/framework/theme/styles/colors.styles.ts +++ b/frontend/src/framework/theme/styles/colors.styles.ts @@ -7,9 +7,8 @@ const colors = { 50: '#e2e1ec', }, brand: { - 900: '#1a365d', - 200: '#b3e0ff', secondary: { + 50: '#3BAF28', 300: '#ff6e1c', 600: '#eb5500', 900: '#e13b00', diff --git a/frontend/src/framework/theme/styles/components.styles.ts b/frontend/src/framework/theme/styles/components.styles.ts index 417d40ef9..070571443 100644 --- a/frontend/src/framework/theme/styles/components.styles.ts +++ b/frontend/src/framework/theme/styles/components.styles.ts @@ -3,7 +3,55 @@ import { colors } from './colors.styles.js'; const components = { Heading: { baseStyle: { - color: colors.text.accent, + color: colors.white, + fontFamily: 'Poppins, sans-serif', + textAlign: 'left', + }, + variants: { + H1: { + fontSize: '54px', + fontWeight: '600', + lineHeight: '64px', + }, + H2: { + fontSize: '30px', + fontWeight: '600', + lineHeight: '32px', + }, + H3: { + fontSize: '18px', + fontWeight: '700', + lineHeight: '27px', + }, + }, + }, + Text: { + baseStyle: { + color: colors.white, + fontFamily: 'Poppins, sans-serif', + textAlign: 'left', + }, + variants: { + body1: { + fontSize: '16px', + fontWeight: '500', + lineHeight: '22px', + }, + bodySmall: { + fontSize: '14px', + fontWeight: '400', + lineHeight: '20px', + }, + caption: { + fontSize: '12px', + fontWeight: '400', + lineHeight: '16px', + }, + button: { + fontSize: '14px', + fontWeight: '600', + lineHeight: '16px', + }, }, }, Button: { @@ -18,6 +66,19 @@ const components = { }, }, }, + primaryOutlined: { + color: colors.background[300], + border: '1px solid', + borderColor: colors.background[300], + _hover: { + color: 'white', + bg: colors.background[300], + _disabled: { + color: colors.background[300], + bg: 'none', + }, + }, + }, ghostIcon: { color: colors.white, _hover: { diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 1412d521b..901c2d1de 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -9,6 +9,7 @@ import { StoreProvider, } from '~/bundles/common/components/components.js'; import { AppRoute } from '~/bundles/common/enums/enums.js'; +import { Studio } from '~/bundles/studio/pages/studio.js'; import { store } from '~/framework/store/store.js'; import { theme } from '~/framework/theme/theme.js'; @@ -29,6 +30,10 @@ const routes = [ path: AppRoute.SIGN_UP, element: , }, + { + path: AppRoute.STUDIO, + element: , + }, ], }, ]; diff --git a/package-lock.json b/package-lock.json index 52258ca3a..c07253538 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,9 +72,12 @@ "frontend": { "version": "1.0.0", "dependencies": { + "@chakra-ui/icons": "2.1.1", "@chakra-ui/react": "2.8.2", "@emotion/react": "11.13.0", "@emotion/styled": "11.13.0", + "@fortawesome/free-solid-svg-icons": "6.6.0", + "@fortawesome/react-fontawesome": "0.2.2", "@reduxjs/toolkit": "2.2.7", "formik": "2.4.6", "framer-motion": "11.3.24", @@ -804,6 +807,18 @@ "react": ">=18" } }, + "node_modules/@chakra-ui/icons": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-2.1.1.tgz", + "integrity": "sha512-3p30hdo4LlRZTT5CwoAJq3G9fHI0wDc0pBaMHj4SUn0yomO+RcDRlzhdXqdr5cVnzax44sqXJVnf3oQG0eI+4g==", + "dependencies": { + "@chakra-ui/icon": "3.2.0" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, "node_modules/@chakra-ui/image": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@chakra-ui/image/-/image-2.1.0.tgz", @@ -2787,6 +2802,49 @@ "yaml": "^2.2.2" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", + "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz", + "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==", + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz", + "integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@gitbeaker/core": { "version": "38.12.1", "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-38.12.1.tgz", diff --git a/shared/src/bundles/users/enums/user-validation-message.enum.ts b/shared/src/bundles/users/enums/user-validation-message.enum.ts index 1a168422a..c821289e6 100644 --- a/shared/src/bundles/users/enums/user-validation-message.enum.ts +++ b/shared/src/bundles/users/enums/user-validation-message.enum.ts @@ -1,8 +1,10 @@ const UserValidationMessage = { EMAIL_REQUIRE: 'Email is required', EMAIL_WRONG: 'Email is wrong', - EMAIL_INVALID: 'Please enter a valid email', + FIELD_REQUIRE: 'Please fill out this field', + EMAIL_INVALID: 'Please enter a valid email', + PASSWORD_LENGTH: 'Password must have from 6 to 12 characters', INVALID_DATA: 'Incorrect email or password. Please try again.', WRONG_CREDENTIALS: 'Email or password are incorrect', diff --git a/shared/src/bundles/users/types/user-sign-in-request-dto.type.ts b/shared/src/bundles/users/types/user-sign-in-request-dto.type.ts index 6d445fe1e..f9e076841 100644 --- a/shared/src/bundles/users/types/user-sign-in-request-dto.type.ts +++ b/shared/src/bundles/users/types/user-sign-in-request-dto.type.ts @@ -1,5 +1,8 @@ -import { type UserSignUpRequestDto } from './types.js'; -type UserSignInRequestDto = Pick; +type UserSignInRequestDto = { + email: string; + password: string; +}; + export { type UserSignInRequestDto }; diff --git a/shared/src/bundles/users/types/user-sign-in-response-dto.type.ts b/shared/src/bundles/users/types/user-sign-in-response-dto.type.ts index 61be88ee0..b768f4b64 100644 --- a/shared/src/bundles/users/types/user-sign-in-response-dto.type.ts +++ b/shared/src/bundles/users/types/user-sign-in-response-dto.type.ts @@ -1,5 +1,9 @@ -import { type UserSignUpResponseDto } from './user-sign-up-response-dto.type.js'; -type UserSignInResponseDto = UserSignUpResponseDto; +type UserSignInResponseDto = { + id: number; + email: string; + token: string; +}; + export { type UserSignInResponseDto }; diff --git a/shared/src/bundles/users/validation-schemas/user-sign-in.validation-schema.ts b/shared/src/bundles/users/validation-schemas/user-sign-in.validation-schema.ts new file mode 100644 index 000000000..100cb3dad --- /dev/null +++ b/shared/src/bundles/users/validation-schemas/user-sign-in.validation-schema.ts @@ -0,0 +1,36 @@ +import { z } from 'zod'; + +import { UserValidationMessage, UserValidationRule } from '../enums/enums.js'; + +type UserSignInRequestValidationDto = { + email: z.ZodString; + password: z.ZodString; +}; + +const userSignIn = z + .object({ + email: z + .string({ required_error: UserValidationMessage.FIELD_REQUIRE }) + .trim() + .min(UserValidationRule.EMAIL_MINIMUM_LENGTH, { + message: UserValidationMessage.EMAIL_INVALID, + }) + .max(UserValidationRule.EMAIL_MAXIMUM_LENGTH, { + message: UserValidationMessage.EMAIL_INVALID, + }) + .email({ + 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, + }), + }) + .required(); + +export { userSignIn }; diff --git a/shared/src/bundles/users/validation-schemas/validation-schemas.ts b/shared/src/bundles/users/validation-schemas/validation-schemas.ts index 58cd817cf..3e53438d4 100644 --- a/shared/src/bundles/users/validation-schemas/validation-schemas.ts +++ b/shared/src/bundles/users/validation-schemas/validation-schemas.ts @@ -1,2 +1,4 @@ -export { userSignIn } from './user-sig-in.validation-schema.js'; + +export { userSignIn } from './user-sign-in.validation-schema.js'; + export { userSignUp } from './user-sign-up.validation-schema.js';