diff --git a/.env.development.local b/.env.development.local index 992c76e4..50bf2636 100644 --- a/.env.development.local +++ b/.env.development.local @@ -1,2 +1 @@ REACT_APP_DOMAIN="https://squiz.pena.digital" -REACT_APP_HUB_DOMAIN="https://shub.pena.digital" \ No newline at end of file diff --git a/.env.production.local b/.env.production.local index 992c76e4..c0284fff 100644 --- a/.env.production.local +++ b/.env.production.local @@ -1,2 +1 @@ -REACT_APP_DOMAIN="https://squiz.pena.digital" -REACT_APP_HUB_DOMAIN="https://shub.pena.digital" \ No newline at end of file +REACT_APP_DOMAIN="https://squiz.pena.digital" \ No newline at end of file diff --git a/package.json b/package.json index b35fa6f2..274e4d00 100755 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@craco/craco": "^7.0.0", "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", - "@frontend/kitui": "^1.0.55", + "@frontend/kitui": "^1.0.62", "@mui/icons-material": "^5.10.14", "@mui/material": "^5.10.14", "@mui/x-date-pickers": "^6.16.1", diff --git a/src/App.tsx b/src/App.tsx index f092e854..c71f16e7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -43,10 +43,12 @@ import { import { enqueueSnackbar } from "notistack"; import PrivateRoute from "@ui_kit/PrivateRoute"; -import { Restore } from "./pages/startPage/Restore"; +import { Restore } from "./pages/auth/Restore"; import { isAxiosError } from "axios"; import { useEffect, useLayoutEffect, useRef } from "react"; +import RecoverPassword from "./pages/auth/RecoverPassword"; +import OutdatedLink from "./pages/auth/OutdatedLink"; export function useUserAccountFetcher({ onError, onNewUserAccount, @@ -183,7 +185,9 @@ export default function App() { } /> } /> - } /> + } /> + } /> + } /> )} @@ -201,9 +205,31 @@ export default function App() { } /> + + } + /> + + } + /> + } /> } /> diff --git a/src/api/auth.ts b/src/api/auth.ts index 7c729a66..23a9301d 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -8,7 +8,7 @@ import type { } from "@frontend/kitui"; import { parseAxiosError } from "../utils/parse-error"; -const apiUrl =process.env.REACT_APP_DOMAIN + "/auth"; +const apiUrl = process.env.REACT_APP_DOMAIN + "/auth"; export async function register( login: string, @@ -70,3 +70,29 @@ export async function logout(): Promise<[unknown, string?]> { return [null, `Не удалось выйти. ${error}`]; } } + +export async function recover( + email: string, +): Promise<[unknown | null, string?]> { + try { + const formData = new FormData(); + formData.append("email", email); + formData.append( + "RedirectionURL", + process.env.REACT_APP_DOMAIN + "/changepwd", + ); + const recoverResponse = await makeRequest({ + url: process.env.REACT_APP_DOMAIN + "/codeword/recover", + body: formData, + useToken: false, + withCredentials: true, + }); + console.log(recoverResponse); + return [recoverResponse]; + } catch (nativeError) { + console.log(nativeError); + const [error] = parseAxiosError(nativeError); + + return [null, `Не удалось восстановить пароль. ${error}`]; + } +} diff --git a/src/api/contactForm.ts b/src/api/contactForm.ts index f64be0c2..00e9face 100644 --- a/src/api/contactForm.ts +++ b/src/api/contactForm.ts @@ -1,6 +1,6 @@ import axios from "axios"; -const domen = process.env.REACT_APP_HUB_DOMAIN +const domen = process.env.REACT_APP_DOMAIN; export function sendContactFormRequest(body: { contact: string; diff --git a/src/api/quizRelase.ts b/src/api/quizRelase.ts index a960f2d5..c846c8d8 100644 --- a/src/api/quizRelase.ts +++ b/src/api/quizRelase.ts @@ -18,7 +18,7 @@ import { CopyQuestionResponse, } from "@model/question/copy"; -const baseUrl = process.env.REACT_APP_DOMAIN ; +const baseUrl = process.env.REACT_APP_DOMAIN; function get(quizId: string) { return makeRequest({ diff --git a/src/model/quiz/quiz.ts b/src/model/quiz/quiz.ts index ada7bbdb..9fb59db5 100644 --- a/src/model/quiz/quiz.ts +++ b/src/model/quiz/quiz.ts @@ -50,7 +50,7 @@ export interface Quiz { question_cnt: number; /** count passings */ passed_count: number; - sessions_count: number; + session_count: number; /** average time of passing */ average_time: number; /** set true if squiz realize group functionality */ @@ -109,7 +109,7 @@ export interface RawQuiz { question_cnt: number; /** count passings */ passed_count: number; - sessions_count: number; + session_count: number; /** average time of passing */ average_time: number; /** set true if squiz realize group functionality */ diff --git a/src/pages/Landing/FooterLanding.tsx b/src/pages/Landing/FooterLanding.tsx index b94d8fd9..0708e15c 100644 --- a/src/pages/Landing/FooterLanding.tsx +++ b/src/pages/Landing/FooterLanding.tsx @@ -127,13 +127,13 @@ export default function Component() { > Пользовательское соглашение - diff --git a/src/pages/Questions/OptionsAndPicture/SettingOptionsAndPict.tsx b/src/pages/Questions/OptionsAndPicture/SettingOptionsAndPict.tsx index 8dff844e..3c3c8269 100644 --- a/src/pages/Questions/OptionsAndPicture/SettingOptionsAndPict.tsx +++ b/src/pages/Questions/OptionsAndPicture/SettingOptionsAndPict.tsx @@ -67,18 +67,18 @@ export default function SettingOptionsAndPict({ > Настройки ответов - - updateQuestion(question.id, (question) => { - if (question.type !== "varimg") return; + {/**/} + {/* updateQuestion(question.id, (question) => {*/} + {/* if (question.type !== "varimg") return;*/} - question.content.own = target.checked; - }) - } - /> + {/* question.content.own = target.checked;*/} + {/* })*/} + {/* }*/} + {/*/>*/} {!isWrappColumn && ( { }} > Настройки результатов - + + + + ); +} diff --git a/src/pages/auth/RecoverPassword.tsx b/src/pages/auth/RecoverPassword.tsx new file mode 100644 index 00000000..89d3d5e3 --- /dev/null +++ b/src/pages/auth/RecoverPassword.tsx @@ -0,0 +1,198 @@ +import { + Box, + Dialog, + IconButton, + Link, + Typography, + useMediaQuery, + useTheme, + Button, +} from "@mui/material"; +import CloseIcon from "@mui/icons-material/Close"; +import { useLocation, useNavigate } from "react-router-dom"; +import { useFormik } from "formik"; +import InputTextfield from "@ui_kit/InputTextfield"; +import PenaLogo from "../Landing/images/icons/QuizLogo"; +import { enqueueSnackbar } from "notistack"; +import { object, string } from "yup"; +import { useEffect, useState } from "react"; +import { useUserStore } from "@root/user"; + +import { getAuthToken, makeRequest, setAuthToken } from "@frontend/kitui"; +import { FormContactFieldName } from "@model/quizSettings"; +import { parseAxiosError } from "@utils/parse-error"; +interface Values { + password: string; +} + +const initialValues: Values = { + password: "", +}; + +const validationSchema = object({ + password: string() + .min(8, "Минимум 8 символов") + .matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы") + .required("Поле обязательно"), +}); + +export default function RecoverPassword() { + const [isDialogOpen, setIsDialogOpen] = useState(true); + const [tokenUser, setTokenUser] = useState(""); + const user = useUserStore((state) => state.user); + const theme = useTheme(); + const upMd = useMediaQuery(theme.breakpoints.up("md")); + const navigate = useNavigate(); + const location = useLocation(); + const formik = useFormik({ + initialValues, + validationSchema, + onSubmit: async (values, formikHelpers) => { + if (tokenUser) { + setAuthToken(tokenUser || ""); + try { + const response = await makeRequest({ + url: process.env.REACT_APP_DOMAIN + "/user/", + method: "PATCH", + body: { password: values.password }, + }); + setIsDialogOpen(false); + navigate("/"); + enqueueSnackbar("Пароль успешно сменён"); + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + setAuthToken(""); + enqueueSnackbar( + `Извините, произошла ошибка, попробуйте повторить позже. ${error}`, + ); + } + } else { + enqueueSnackbar("Неверный url-адрес"); + } + }, + }); + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const authToken = params.get("auth"); + setTokenUser(authToken); + + history.pushState(null, document.title, "/changepwd"); + return () => { + setAuthToken(""); + }; + }, []); + console.log(tokenUser); + + function handleClose() { + setIsDialogOpen(false); + setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen); + } + + return ( + + + + + + + + + + Введите новый пароль + + + + + + + ); +} diff --git a/src/pages/startPage/Restore.tsx b/src/pages/auth/Restore.tsx similarity index 69% rename from src/pages/startPage/Restore.tsx rename to src/pages/auth/Restore.tsx index be518193..10b89d17 100644 --- a/src/pages/startPage/Restore.tsx +++ b/src/pages/auth/Restore.tsx @@ -11,35 +11,25 @@ import { useTheme, } from "@mui/material"; import InputTextfield from "@ui_kit/InputTextfield"; -import PasswordInput from "@ui_kit/passwordInput"; import { useFormik } from "formik"; import { object, ref, string } from "yup"; import Logotip from "../Landing/images/icons/QuizLogo"; import { useNavigate, Link as RouterLink, useLocation } from "react-router-dom"; +import { enqueueSnackbar } from "notistack"; +import { recover } from "@api/auth"; interface Values { email: string; - password: string; - repeatPassword: string; } const initialValues: Values = { email: "", - password: "", - repeatPassword: "", }; const validationSchema = object({ email: string() .required("Поле обязательно") .email("Введите корректный email"), - password: string() - .min(8, "Минимум 8 символов") - .matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы") - .required("Поле обязательно"), - repeatPassword: string() - .oneOf([ref("password"), undefined], "Пароли не совпадают") - .required("Повторите пароль"), }); export const Restore: FC = () => { @@ -52,7 +42,19 @@ export const Restore: FC = () => { const formik = useFormik({ initialValues, validationSchema, - onSubmit: (values) => {}, + onSubmit: async (values, formikHelpers) => { + const [recoverResponse, recoverError] = await recover( + values.email.trim(), + ); + + formikHelpers.setSubmitting(false); + if (recoverError) { + enqueueSnackbar(recoverError); + return; + } + navigate("/"); + enqueueSnackbar("Письмо прийдёт Вам на почту"); + }, }); function handleClose() { @@ -138,41 +140,6 @@ export const Restore: FC = () => { label="Email" gap={upMd ? "10px" : "10px"} /> - -