diff --git a/template.json b/template.json index cf002ea..f03e819 100644 --- a/template.json +++ b/template.json @@ -8,19 +8,20 @@ "@emotion/styled": "^11.10.5", "@formatjs/intl-pluralrules": "5.1.4", "@formatjs/intl-relativetimeformat": "11.1.4", + "@hookform/resolvers": "^3.1.0", "@mui/icons-material": "^5.11.0", "@mui/lab": "^5.0.0-alpha.115", "@mui/material": "^5.11.4", "axios": "^1.3.4", "axios-auth-refresh": "^3.3.6", "dayjs": "^1.10.7", - "formik": "^2.2.9", "intl": "^1.2.5", "license-checker-webpack-plugin": "^0.2.1", "lodash": "^4.17.21", "query-string": "^7.0.1", "react": "18.2.0", "react-dom": "18.2.0", + "react-hook-form": "^7.43.9", "react-intl": "6.2.1", "react-query": "^3.39.3", "react-router": "^6.4.2", diff --git a/template/src/components/app/router/AppRouter.tsx b/template/src/components/app/router/AppRouter.tsx index a121388..3fda927 100644 --- a/template/src/components/app/router/AppRouter.tsx +++ b/template/src/components/app/router/AppRouter.tsx @@ -1,6 +1,7 @@ import { ReactQueryDevtools } from "react-query/devtools"; import { BrowserRouter, Outlet, Route, Routes } from "react-router-dom"; import { BASE_NAME, DEBUG_PUBLIC_DASHBOARD, LOADING_INDICATOR_DELAY_MS } from "../../../config"; +import { useQueryParams } from "../../../hooks/useQueryParams"; import { IDebugTab, useDebugStore } from "../../../stores/debugStore"; import { useGeneralStore } from "../../../stores/generalStore"; import { AuthLoginSite } from "../../auth/sites/AuthLoginSite"; @@ -16,7 +17,6 @@ import { NoAuthOnlyRoute } from "./NoAuthOnlyRoute"; import { PrivateRoute } from "./PrivateRoute"; import { RoutingManager } from "./RoutingManager"; import ScrollToTop from "./ScrollToTop"; -import { useQueryParams } from "../../../hooks/useQueryParams"; export const AppRouter = () => { const isLoading = useGeneralStore((state) => state.isLoading); diff --git a/template/src/components/auth/sites/AuthLoginSite.tsx b/template/src/components/auth/sites/AuthLoginSite.tsx index 90256a6..e1b80c1 100644 --- a/template/src/components/auth/sites/AuthLoginSite.tsx +++ b/template/src/components/auth/sites/AuthLoginSite.tsx @@ -1,7 +1,8 @@ +import { yupResolver } from "@hookform/resolvers/yup"; import { Button } from "@mui/material"; import { AxiosError } from "axios"; -import { Field, Form, Formik } from "formik"; import * as React from "react"; +import { useForm } from "react-hook-form"; import * as Yup from "yup"; import { t } from "../../../i18n/util"; import { useLogin } from "../../../network/api/useLogin"; @@ -19,6 +20,25 @@ interface ILoginValues { } export const AuthLoginSite = () => { + const { handleSubmit, formState, control } = useForm({ + defaultValues: { + email: "", + password: "", + }, + mode: "onTouched", + resolver: yupResolver( + Yup.object().shape({ + email: Yup.string() + .email(t("screen.login.form.email.validation_error")) + .required(t("screen.login.form.email.validation_error")) + .trim(), + password: Yup.string() + .min(6, t("screen.login.form.password.validation_error")) + .required(t("screen.login.form.password.validation_error")), + }), + ), + }); + const [error, setError] = React.useState(); const pushRoute = usePushRoute(); @@ -28,7 +48,7 @@ export const AuthLoginSite = () => { const setIsLoading = useGeneralStore((state) => state.setIsLoading); - const handleSubmit = async (model: ILoginValues) => { + const onSubmit = async (model: ILoginValues) => { setIsLoading(true); setError(""); @@ -84,64 +104,42 @@ export const AuthLoginSite = () => { > {t("screen.login.title")} -
- + + + + + {error &&
{error}
} + - - )} -
-
+ {t("screen.login.form.submit")} + + ); diff --git a/template/src/components/ui/CustomInputField.tsx b/template/src/components/ui/CustomInputField.tsx index b15cacd..096d8a2 100644 --- a/template/src/components/ui/CustomInputField.tsx +++ b/template/src/components/ui/CustomInputField.tsx @@ -1,17 +1,16 @@ import { MenuItem, TextField, TextFieldProps } from "@mui/material"; -import { FieldInputProps, FormikState, getIn } from "formik"; import * as React from "react"; +import { FieldValues, UseControllerProps, useController } from "react-hook-form"; import { FieldError } from "./FieldError"; -type IProps = TextFieldProps & { - field: FieldInputProps; - onChange?: () => void; - form: FormikState; - showValidationErrorText?: boolean; - selectOptions?: { value: string; label: string }[]; -}; +type IProps = TextFieldProps & + UseControllerProps & { + onChange?: () => void; + showValidationErrorText?: boolean; + selectOptions?: { value: string; label: string }[]; + }; -export const CustomInputField = ({ +export const CustomInputField = ({ style, label, type, @@ -20,16 +19,17 @@ export const CustomInputField = ({ minRows, maxRows, required, - form, - field, + name, + control, "aria-label": ariaLabel, placeholder, showValidationErrorText = true, selectOptions, onChange, -}: IProps) => { - const fieldError = getIn(form.errors, field.name); - const showError = getIn(form.touched, field.name) && !!fieldError; +}: IProps) => { + const { field, fieldState } = useController({ control, name: name ?? "" }); + + const fieldError = fieldState.error?.message; const handleChange = (event: React.ChangeEvent) => { field.onChange(event); @@ -42,14 +42,10 @@ export const CustomInputField = ({ {selectOptions?.map((selectOption) => ( @@ -67,7 +67,7 @@ export const CustomInputField = ({ ))} - {showValidationErrorText && {showError ? fieldError : ""}} + {showValidationErrorText && {fieldError}} ); };