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; -
-