This commit is contained in:
kuwsh1n 2024-04-07 00:38:07 +03:00
parent 6817f5bf9b
commit 33f27d2efc
23 changed files with 570 additions and 241 deletions

7
Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM node:21-alpine
WORKDIR /frontend
COPY package.json .
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "start:dev"]

View File

@ -32,9 +32,11 @@
"@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"axios": "^1.6.8",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.10.1", "react-bootstrap": "^2.10.1",
"react-cookie": "^7.1.4",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.22.1" "react-router-dom": "^6.22.1"
} }

View File

@ -1,7 +1,9 @@
import React, { useState, useEffect, useLayoutEffect } from "react"; import React, { useState, useEffect, useLayoutEffect } from "react";
import { Outlet, useNavigate } from "react-router-dom"; import { Outlet, useNavigate } from "react-router-dom";
import { FormsData, UserData, TypeAnswerData } from "./context"; import { FormsData, UserData, TypeAnswerData } from "./context";
import { useCookies } from "react-cookie";
import { globalRender } from "./router/protectedRouting.js"; import { globalRender } from "./router/protectedRouting.js";
import { verifyUserApi } from "./hooks/api/enterAccountApi.js"
import classes from "./assets/styles/app.module.scss" import classes from "./assets/styles/app.module.scss"
import NavBar from "./components/NavBar.jsx"; import NavBar from "./components/NavBar.jsx";
import 'bootstrap/dist/css/bootstrap.min.css'; import 'bootstrap/dist/css/bootstrap.min.css';
@ -30,6 +32,24 @@ const App = () => {
]); ]);
// useEffect(() => globalRender(window.location.pathname, user, navigate)); // useEffect(() => globalRender(window.location.pathname, user, navigate));
const [cookies, _, __] = useCookies(["user"]);
useEffect(() => {
async function verifyUser() {
const response = await verifyUserApi(cookies.token);
if (response) {
if (response.status === 200) {
setUser(response.data);
}
else {
console.log(response)
}
}
}
verifyUser()
}, [])
return ( return (
<UserData.Provider value={{ user, setUser }}> <UserData.Provider value={{ user, setUser }}>

View File

@ -12,9 +12,38 @@
.header { .header {
text-align: right; text-align: right;
display: flex; display: flex;
justify-content: end; justify-content: space-between;
width: 100%; width: 100%;
height: 8%; height: 8%;
&__date {
width: 30%;
height: 100%;
&__item {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
span {
position: absolute;
height: 100%;
font-size: 8px;
font-family: "Montserrat", sans-serif;
top: -40%;
left: 2%;
}
input {
display: block;
height: 100%;
font-size: 15px;
font-family: "Montserrat", sans-serif;
border: 1px solid rgb(200, 200, 200);
padding: 1%;
border-radius: 5px;
}
}
}
&__listBtn { &__listBtn {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -5,9 +5,9 @@ import MyButton from "./MyButton.jsx"
const AnswerModal = ({ const AnswerModal = ({
cleanStates, cleanStates,
currentTypeAnswer, currentTypeAnswer,
updateAnswerByForm, updateBlock,
stateModal, stateModal,
saveStates, addFormBlock,
question, question,
file, file,
listTypeAnswer, listTypeAnswer,
@ -16,18 +16,15 @@ const AnswerModal = ({
currentOptionAnswer, currentOptionAnswer,
setCurrentOptionAnswer, setCurrentOptionAnswer,
comment, comment,
datetime,
mandatory, mandatory,
setMandatory, setMandatory,
addOptionAnswer, addOptionAnswer,
setQuestion, setQuestion,
setComment, setComment,
setDatetime,
setCurrentTypeAnswer, setCurrentTypeAnswer,
setFile setFile
}) => { }) => {
return ( return (
<div class="modal fade myModal" className={classes.myModal} id="answerModal" tabIndex="-1" aria-labelledby="exampleModalLabel" data-bs-backdrop="static" aria-hidden="true"> <div class="modal fade myModal" className={classes.myModal} id="answerModal" tabIndex="-1" aria-labelledby="exampleModalLabel" data-bs-backdrop="static" aria-hidden="true">
<div class="modal-dialog myModal__dialog" className={classes.myModal__dialog}> <div class="modal-dialog myModal__dialog" className={classes.myModal__dialog}>
@ -79,14 +76,10 @@ const AnswerModal = ({
<span className={classes.myModal__dialog__content__body__mandatory__title}>Обязательный вопрос</span> <span className={classes.myModal__dialog__content__body__mandatory__title}>Обязательный вопрос</span>
<input className={classes.myModal__dialog__content__body__mandatory__choice} type="checkbox" checked={mandatory} onChange={() => setMandatory(!mandatory)}/> <input className={classes.myModal__dialog__content__body__mandatory__choice} type="checkbox" checked={mandatory} onChange={() => setMandatory(!mandatory)}/>
</div> </div>
<div className={classes.myModal__dialog__content__body__time}>
<span>Дедлайн выполнения</span>
<input type="datetime-local" value={datetime} onChange={event => setDatetime(event.target.value)}/>
</div>
</div> </div>
<div class="modal-footer myModal__dialog__content__footer" className={classes.myModal__dialog__content__footer}> <div class="modal-footer myModal__dialog__content__footer" className={classes.myModal__dialog__content__footer}>
<button type="button" class="btn btn-light" data-bs-dismiss="modal" onClick={cleanStates}>Отмена</button> <button type="button" class="btn btn-light" data-bs-dismiss="modal" onClick={cleanStates}>Отмена</button>
<button type="button" class="btn" style={{color: 'white', backgroundColor: 'rgb(150, 209, 158)'}} data-bs-dismiss="modal" onClick={stateModal ? updateAnswerByForm : saveStates}>Сохранить</button> <button type="button" class="btn" style={{color: 'white', backgroundColor: 'rgb(150, 209, 158)'}} data-bs-dismiss="modal" onClick={stateModal ? updateBlock : addFormBlock}>Сохранить</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,9 +1,9 @@
import React, { useState } from "react"; import React, { useState } from "react";
import classes from "../assets/styles/generatingFormFields.module.scss"; import classes from "../assets/styles/generatingFormFields.module.scss";
const GeneratingFormFields = ({newForm, listTypeAnswer, answers, updateAnswersForm}) => { const GeneratingFormFields = ({listBlock, listTypeAnswer, answers, updateAnswersForm}) => {
return ( return (
newForm.map((item, i) => listBlock.map((item, i) =>
<div className={classes.item} key={i}> <div className={classes.item} key={i}>
<div className={classes.item__question}> <div className={classes.item__question}>
<p className={classes.item__question__text}>{i + 1}) {item.question}</p> <p className={classes.item__question__text}>{i + 1}) {item.question}</p>

View File

@ -16,7 +16,7 @@ const NavBar = ({navigate, auth, setAuth}) => {
<div className={classes.profile}> <div className={classes.profile}>
{auth ? {auth ?
<div className={classes.profile__authorized}> <div className={classes.profile__authorized}>
<span onClick={() => navigate("/profile")}>Профиль ({auth.name})</span> <span onClick={() => navigate("/profile")}>Профиль ({auth.login})</span>
</div> : </div> :
<div className={classes.profile__nonAuthorized}> <div className={classes.profile__nonAuthorized}>
<span onClick={() => navigate("/enter")}>Вход <i class="fa-solid fa-arrow-right-to-bracket"></i></span> <span onClick={() => navigate("/enter")}>Вход <i class="fa-solid fa-arrow-right-to-bracket"></i></span>

View File

@ -2,7 +2,7 @@ import React from "react";
import classes from "../assets/styles/components/previewModal.module.scss"; import classes from "../assets/styles/components/previewModal.module.scss";
import GeneratingFormFields from "./GeneratingFormFields.jsx"; import GeneratingFormFields from "./GeneratingFormFields.jsx";
const PreviewModal = ({newForm, listTypeAnswer}) => { const PreviewModal = ({listBlock, listTypeAnswer}) => {
return ( return (
<div class="modal fade modal-lg" className={classes.myModal} id="previewModal" tabIndex="-1" aria-labelledby="exampleModalLabel" data-bs-backdrop="static" aria-hidden="true"> <div class="modal fade modal-lg" className={classes.myModal} id="previewModal" tabIndex="-1" aria-labelledby="exampleModalLabel" data-bs-backdrop="static" aria-hidden="true">
<div class="modal-dialog" className={classes.myModal__dialog}> <div class="modal-dialog" className={classes.myModal__dialog}>
@ -12,7 +12,7 @@ const PreviewModal = ({newForm, listTypeAnswer}) => {
<i class="fa-solid fa-xmark" data-bs-dismiss="modal" aria-label="Close"></i> <i class="fa-solid fa-xmark" data-bs-dismiss="modal" aria-label="Close"></i>
</div> </div>
<div class="modal-body" className={classes.myModal__dialog__content__body}> <div class="modal-body" className={classes.myModal__dialog__content__body}>
<GeneratingFormFields newForm={newForm} listTypeAnswer={listTypeAnswer}/> <GeneratingFormFields listBlock={listBlock} listTypeAnswer={listTypeAnswer}/>
</div> </div>
<div class="modal-footer" className={classes.myModal__dialog__content__footer}> <div class="modal-footer" className={classes.myModal__dialog__content__footer}>

View File

@ -2,6 +2,7 @@ import React, { useState } from "react";
import classes from "../../assets/styles/components/typeAnswer/dropDownList.module.scss" import classes from "../../assets/styles/components/typeAnswer/dropDownList.module.scss"
const DropDownList = ({postfix, optionAnswer, answers, updateAnswersForm}) => { const DropDownList = ({postfix, optionAnswer, answers, updateAnswersForm}) => {
console.log(optionAnswer)
return ( return (
<div className={classes.main}> <div className={classes.main}>
<select <select
@ -9,6 +10,7 @@ const DropDownList = ({postfix, optionAnswer, answers, updateAnswersForm}) => {
onChange={updateAnswersForm ? (e) => updateAnswersForm(Number(e.target.value), postfix) : () => {}} onChange={updateAnswersForm ? (e) => updateAnswersForm(Number(e.target.value), postfix) : () => {}}
> >
{optionAnswer.map((item, i) => {optionAnswer.map((item, i) =>
// <option value={item.id ? item.id : item[0].Value} key={i}>{item.text ? item.text : item[1].Value}</option>
<option value={item.id} key={i}>{item.text}</option> <option value={item.id} key={i}>{item.text}</option>
)} )}
</select> </select>

View File

@ -34,6 +34,7 @@ const InputMultipleRadio = ({postfix, optionAnswer, answers, updateAnswersForm})
checked={answers ? checkRadio(i) : false} checked={answers ? checkRadio(i) : false}
onChange={() => updateStateCheckbox(i)} onChange={() => updateStateCheckbox(i)}
/> />
{/* <label class="form-check-label" for={`inputMultiple_${postfix}`}>{item.text ? item.text : item[1].Value}</label> */}
<label class="form-check-label" for={`inputMultiple_${postfix}`}>{item.text}</label> <label class="form-check-label" for={`inputMultiple_${postfix}`}>{item.text}</label>
</div> </div>
)} )}

View File

@ -15,6 +15,7 @@ const InputRadio = ({postfix, optionAnswer, answers, updateAnswersForm}) => {
checked={answers ? answers[postfix].answer === i : false} checked={answers ? answers[postfix].answer === i : false}
onChange={updateAnswersForm ? (e) => updateAnswersForm(Number(e.target.value), postfix) : () => {}} onChange={updateAnswersForm ? (e) => updateAnswersForm(Number(e.target.value), postfix) : () => {}}
/> />
{/* <label class="form-check-label" for={`inputRadio_${postfix}`}>{item.text ? item.text : item[1].Value}</label> */}
<label class="form-check-label" for={`inputRadio_${postfix}`}>{item.text}</label> <label class="form-check-label" for={`inputRadio_${postfix}`}>{item.text}</label>
</div> </div>
)} )}

View File

@ -1,43 +1,46 @@
import { totalRegisterValidate } from "../validation/enterAccountValidate.js"; import { totalRegisterValidate } from "../validation/enterAccountValidate.js";
import axios from "axios";
async function logIn(email, password) { async function logIn(login, password) {
return new Promise((resolve, reject) => { try {
setTimeout(() => { const response = await axios.post("http://localhost:8080/auth/signIn", {"login": login, "password": password})
if (true) { return response
resolve({
email: "senya.bogachev@mail.ru",
phone: "89110128244",
name: "Арсений",
surname: "Богачев",
patronymic: "Валерьевич"
})
} }
else { catch (e) {
reject("Error") return e
} }
}, 1000)
}).catch((error) => {
console.log(error)
})
}; };
async function completeRegistration(data) { async function completeRegistration(data) {
const validate = totalRegisterValidate(data) const validate = totalRegisterValidate(data);
return new Promise((resolve, reject) => {
setTimeout(() => {
if (validate.status) { if (validate.status) {
console.log("Отправляем данные на бэк ->", data) try {
resolve({ const response = await axios.post("http://localhost:8080/auth/signUp", {"login": data.login, "password": data.password})
status: 201, return response
data: data
})
} }
else { catch (e) {
reject(validate.message) return e
} }
}, 1000) }
}) return validate.message
}; };
export { logIn, completeRegistration }; async function verifyUserApi(token=false) {
if (token) {
try {
const response = await axios.get("http://localhost:8080/auth/me", {
headers: {
"Authorization": `Token ${token}`,
},
})
return response
}
catch(e) {
return e
}
}
return false
}
export { logIn, completeRegistration, verifyUserApi };

View File

@ -1,65 +1,87 @@
function saveAnswersApi(id, answers) { import axios from "axios";
return new Promise((resolve, reject) => {
setTimeout(() => { async function listFormBlockApi(token, formId) {
if (true) { try {
resolve({ const response = await axios.get(`http://localhost:8080/formBuilder/edit/${formId}/list`,
id: id, {
answers: answers headers: {
}) "Authorization": `Token ${token}`,
} }
else {
reject("Error")
}
}, 1000)
}) })
return response
}
catch (e) {
return e
}
} }
function removeFormApi(id) { async function addFormBlockApi(token, formId, data) {
return new Promise((resolve, reject) => { try {
setTimeout(() => { const response = await axios.post(`http://localhost:8080/formBuilder/edit/${formId}/add`,
if (true) { {
resolve({ data: data
id: id },
}) {
headers: {
"Authorization": `Token ${token}`,
} }
else {
reject("Error")
}
}, 200)
}) })
return response
}
catch (e) {
return e
}
};
// function removeFormApi(id) {
// return new Promise((resolve, reject) => {
// setTimeout(() => {
// if (true) {
// resolve({
// id: id
// })
// }
// else {
// reject("Error")
// }
// }, 200)
// })
// };
async function updateBlockApi(token, blockId, data) {
try {
const response = await axios.post(`http://localhost:8080/formBuilder/edit/${blockId}/set`,
{
data: data
},
{
headers: {
"Authorization": `Token ${token}`,
}
})
return response
}
catch (e) {
return e
}
} }
function updateFormByFormsApi(id, name, questions) { async function saveFormApi(token) {
return new Promise((resolve, reject) => { try {
setTimeout(() => { const response = await axios.post("http://localhost:8080/formBuilder/new",
if (true) { {
resolve({ title: "Новая форма"
id: id, },
name: name, {
questions: questions headers: {
}) "Authorization": `Token ${token}`
} }
else {
reject("Error")
}
}, 1000)
}) })
} return response
}
catch (e) {
return e
}
};
function saveFormApi(name, questions) { export { addFormBlockApi, listFormBlockApi, saveFormApi, updateBlockApi }
return new Promise((resolve, reject) => {
setTimeout(() => {
if (true) {
resolve({
name: name,
questions: questions
})
}
else {
reject("Error")
}
}, 1000)
})
}
export { saveAnswersApi, saveFormApi, updateFormByFormsApi, removeFormApi }

View File

@ -0,0 +1,50 @@
import axios from "axios";
async function listFormsApi(token) {
try {
const response = await axios.get("http://localhost:8080/formBuilder/list", {
headers: {
"Authorization": `Token ${token}`,
}
})
return response
}
catch (e) {
return e
}
};
async function createFormApi(token) {
try {
const response = await axios.post("http://localhost:8080/formBuilder/new",
{
title: "Новая форма"
},
{
headers: {
"Authorization": `Token ${token}`,
}
})
return response
}
catch (e) {
return e
}
};
async function removeFormApi(token, formId) {
try {
const response = await axios.post(`http://localhost:8080/formBuilder/edit/${formId}/delete`, {
headers: {
"Authorization": `Token ${token}`,
},
})
return response
}
catch (e) {
return e
}
};
export { listFormsApi, createFormApi, removeFormApi };

View File

@ -0,0 +1,30 @@
function responseDataToListBlock(data) {
const result = []
for (let block of data) {
const newParam = {
id: block.id,
formId: block.form_id,
order: block.order
}
for (let param of block.data) {
if (Array.isArray(param.Value) && param.Value.length) {
newParam[param.Key] = param.Value.map(item => ({
id: item[0].Value,
text: item[1].Value
}))
}
else {
newParam[param.Key] = param.Value
}
}
result.push(newParam)
}
return result
}
export { responseDataToListBlock }

View File

@ -12,12 +12,12 @@ const constructorAnswerValidate = (state, messageReject = "Ошибка", messag
const totalRegisterValidate = (data) => { const totalRegisterValidate = (data) => {
const listValidation = [ const listValidation = [
constructorAnswerValidate(data.name.length, "Обязательное поле."), constructorAnswerValidate(data.login.length, "Обязательное поле."),
constructorAnswerValidate(data.surname.length, "Обязательное поле."), // constructorAnswerValidate(data.surname.length, "Обязательное поле."),
constructorAnswerValidate(data.email.length, "Обязательное поле."), // constructorAnswerValidate(data.email.length, "Обязательное поле."),
constructorAnswerValidate(data.phone.length, "Обязательное поле."), // constructorAnswerValidate(data.phone.length, "Обязательное поле."),
constructorAnswerValidate(data.password === data.repiedPassword, "Введенные пароли не совпадают."), constructorAnswerValidate(data.password === data.repiedPassword, "Введенные пароли не совпадают."),
constructorAnswerValidate(data.password < 8, "Пароль должен иметь более 8 символов.") constructorAnswerValidate(data.password >= 8, "Пароль должен иметь более 8 символов.")
] ]
for (let value of listValidation) { for (let value of listValidation) {

View File

@ -1,4 +1,5 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext } from "react";
import { useCookies } from "react-cookie";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import classes from "../assets/styles/enterAccount.module.scss"; import classes from "../assets/styles/enterAccount.module.scss";
import MyInput from "../components/MyInput.jsx"; import MyInput from "../components/MyInput.jsx";
@ -12,18 +13,19 @@ const EnterAccount = () => {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [phone, setPhone] = useState(""); const [phone, setPhone] = useState("");
const [name, setName] = useState(""); const [login, setLogin] = useState("");
const [surname, setSurname] = useState(""); const [surname, setSurname] = useState("");
const [patronymic, setPatronymic] = useState(""); const [patronymic, setPatronymic] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [repiedPassword, setRepiedPassword] = useState(""); const [repiedPassword, setRepiedPassword] = useState("");
const {user, setUser} = useContext(UserData); const {user, setUser} = useContext(UserData);
const [cookies, setCookie, removeCookie] = useCookies(["user"]);
function cleanState() { function cleanState() {
setEmail(""); setEmail("");
setPhone(""); setPhone("");
setName(""); setLogin("");
setSurname(""); setSurname("");
setPatronymic(""); setPatronymic("");
setPassword(""); setPassword("");
@ -35,26 +37,52 @@ const EnterAccount = () => {
cleanState(); cleanState();
}; };
function createUser() { async function createUser() {
completeRegistration({ const response = await completeRegistration({
// email: email,
// phone: phone,
login: login,
// surname: surname,
// patronymic: patronymic,
password: password,
repiedPassword: repiedPassword
});
if (response.status === 200) {
setUser({
email: email, email: email,
phone: phone, phone: phone,
name: name, login: login,
surname: surname, surname: surname,
patronymic: patronymic, patronymic: patronymic,
password: password, password: password,
repiedPassword: repiedPassword });
}).then((resolve, reject) => { setCookie("token", response.data.token);
if (resolve.status === 201) {
setUser(resolve.data)
cleanState(); cleanState();
navigate("/"); navigate("/");
} }
}).catch((error) => { else {
console.log(error) console.log("Error")
}) }
}; };
async function logInToAccount() {
const response = await logIn(login, password)
if (response.status === 200) {
setCookie("token", response.data.token);
cleanState();
setUser({
login: login
})
navigate("/")
// window.location.reload()
}
else {
console.log(response)
}
}
return ( return (
<div className={classes.main}> <div className={classes.main}>
<div className={classes.wrapper}> <div className={classes.wrapper}>
@ -75,14 +103,14 @@ const EnterAccount = () => {
<h3>Войти в аккаунт</h3> <h3>Войти в аккаунт</h3>
</div> </div>
<div className={classes.content__wrapper__login__body}> <div className={classes.content__wrapper__login__body}>
<MyInput placeholder={"Email"} otherMainStyle={{width: "100%", height: "20%"}} otherInputStyle={{width: "100%"}} value={email} change={setEmail}/> <MyInput placeholder={"Логин"} otherMainStyle={{width: "100%", height: "20%"}} otherInputStyle={{width: "100%"}} value={login} change={setLogin}/>
<MyInput type={"password"} placeholder={"Пароль"} otherMainStyle={{width: "100%", height: "20%"}} otherInputStyle={{width: "100%"}} value={password} change={setPassword}/> <MyInput type={"password"} placeholder={"Пароль"} otherMainStyle={{width: "100%", height: "20%"}} otherInputStyle={{width: "100%"}} value={password} change={setPassword}/>
</div> </div>
<div className={classes.content__wrapper__login__footer}> <div className={classes.content__wrapper__login__footer}>
<MyButton <MyButton
text={"Войти"} text={"Войти"}
otherStyle={{height: "50%", width: "20%"}} otherStyle={{height: "50%", width: "20%"}}
click={() => logIn(email, password).then((resolve, reject) => setUser(resolve))} click={logInToAccount}
/> />
</div> </div>
</div> : </div> :
@ -94,7 +122,7 @@ const EnterAccount = () => {
<MyInput placeholder={"Email"} otherMainStyle={{width: "100%", height: "15%"}} otherInputStyle={{width: "100%"}} value={email} change={setEmail}/> <MyInput placeholder={"Email"} otherMainStyle={{width: "100%", height: "15%"}} otherInputStyle={{width: "100%"}} value={email} change={setEmail}/>
<MyInput placeholder={"Номер телефона"} otherMainStyle={{width: "100%", height: "15%"}} otherInputStyle={{width: "100%"}} value={phone} change={setPhone}/> <MyInput placeholder={"Номер телефона"} otherMainStyle={{width: "100%", height: "15%"}} otherInputStyle={{width: "100%"}} value={phone} change={setPhone}/>
<div className={classes.content__wrapper__register__body__fio}> <div className={classes.content__wrapper__register__body__fio}>
<MyInput placeholder={"Имя"} otherMainStyle={{width: "32%", height: "100%"}} otherInputStyle={{width: "100%"}} value={name} change={setName}/> <MyInput placeholder={"Логин"} otherMainStyle={{width: "32%", height: "100%"}} otherInputStyle={{width: "100%"}} value={login} change={setLogin}/>
<MyInput placeholder={"Фамилия"} otherMainStyle={{width: "32%", height: "100%"}} otherInputStyle={{width: "100%"}} value={surname} change={setSurname}/> <MyInput placeholder={"Фамилия"} otherMainStyle={{width: "32%", height: "100%"}} otherInputStyle={{width: "100%"}} value={surname} change={setSurname}/>
<MyInput placeholder={"Отчество (при наличии)"} otherMainStyle={{width: "32%", height: "100%"}} otherInputStyle={{width: "100%"}} value={patronymic} change={setPatronymic}/> <MyInput placeholder={"Отчество (при наличии)"} otherMainStyle={{width: "32%", height: "100%"}} otherInputStyle={{width: "100%"}} value={patronymic} change={setPatronymic}/>
</div> </div>

View File

@ -1,10 +1,11 @@
import React, { useState, useContext, useEffect } from "react"; import React, { useState, useContext, useEffect } from "react";
import { useCookies } from "react-cookie";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import classes from "../assets/styles/forms.module.scss" import classes from "../assets/styles/forms.module.scss"
import MyButton from "../components/MyButton.jsx"; import MyButton from "../components/MyButton.jsx";
import MyInput from "../components/MyInput.jsx"; import MyInput from "../components/MyInput.jsx";
import { FormsData, UserData } from "../context"; import { FormsData, UserData } from "../context";
import { removeFormApi } from "../hooks/api/formApi.js"; import { listFormsApi, createFormApi, removeFormApi } from "../hooks/api/listFormsApi.js";
const Forms = () => { const Forms = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -12,39 +13,53 @@ const Forms = () => {
const {user, setUser} = useContext(UserData); const {user, setUser} = useContext(UserData);
const [stateLoading, setStateLoading] = useState(false); const [stateLoading, setStateLoading] = useState(false);
const response = ms => { const [cookies, _, __] = useCookies(["user"]);
return new Promise(r => setTimeout(() => r('response end'), ms))
useEffect(() => {
async function listForms() {
const response = await listFormsApi(cookies.token)
if (response.data) {
setForms(response.data)
}
else if (response.status === 200 && response.data) {
setForms([])
}
else {
console.log(response)
}
}; };
function createForm() { listForms();
}, [])
async function createForm() {
setStateLoading(true); setStateLoading(true);
response(1000) const response = await createFormApi(cookies.token)
.then((r) => {
console.log(r);
setStateLoading(false); setStateLoading(false);
navigate("/forms/edit");
if (response.data) {
navigate(`/forms/${response.data.id}/edit`)
}
else {
console.log(response)
} }
)
}; };
function editForm(item) { function editForm(item) {
navigate("/forms/edit", { navigate(`/forms/${item.id}/edit`);
state: {
id: item.id,
data: item.questions
}
});
}; };
function removeForm(id) { async function removeForm(id) {
removeFormApi(id) setForms([...forms.filter(item => item.id !== id)]);
.then((resolve, _) => { // const response = await removeFormApi(cookies.token, id)
console.log(resolve);
setForms([...forms.filter(item => { // if (response.status === 200) {
item.id !== id // setForms([...forms.filter(item => item.id !== id)]);
})]); // }
}) // else {
.catch(error => console.log(error)); // console.log(response)
// }
}; };
return ( return (

View File

@ -1,9 +1,13 @@
import React from "react"; import React, { useState } from "react";
import { useNavigate, useLocation, useParams } from 'react-router-dom';
import classes from "../assets/styles/home.module.scss"; import classes from "../assets/styles/home.module.scss";
import MyButton from "../components/MyButton.jsx"; import MyButton from "../components/MyButton.jsx";
import MyInput from "../components/MyInput.jsx"; import MyInput from "../components/MyInput.jsx";
const Home = () => { const Home = () => {
const [token, setToken] = useState("");
const navigate = useNavigate();
return ( return (
<div className={classes.main}> <div className={classes.main}>
<div className={classes.wrapper}> <div className={classes.wrapper}>
@ -19,12 +23,12 @@ const Home = () => {
</div> </div>
<div className={classes.content__token}> <div className={classes.content__token}>
<div className={classes.content__token__wrapper}> <div className={classes.content__token__wrapper}>
<MyInput placeholder={"Введите токен формы..."} otherMainStyle={{width: "100%"}} otherInputStyle={{width: "100%"}}/> <MyInput placeholder={"Введите токен формы..."} otherMainStyle={{width: "100%"}} otherInputStyle={{width: "100%"}} value={token} change={(e) => setToken(e)}/>
</div> </div>
</div> </div>
<div className={classes.content__search}> <div className={classes.content__search}>
<div className={classes.content__search__wrapper}> <div className={classes.content__search__wrapper}>
<MyButton text={"Найти форму"}/> <MyButton text={"Найти форму"} click={() => navigate(`/forms/${token}`)}/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,18 +1,23 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext, useEffect } from "react";
import { useNavigate, useLocation } from 'react-router-dom'; import { useNavigate, useLocation, useParams } from 'react-router-dom';
import { useCookies } from "react-cookie";
import classes from "../assets/styles/newForm.module.scss"; import classes from "../assets/styles/newForm.module.scss";
import MyButton from "../components/MyButton.jsx"; import MyButton from "../components/MyButton.jsx";
import AnswerModal from "../components/AnswerModal.jsx"; import AnswerModal from "../components/AnswerModal.jsx";
import PreviewModal from "../components/PreviewModal.jsx" import PreviewModal from "../components/PreviewModal.jsx"
import { FormsData, TypeAnswerData } from "../context"; import { FormsData, TypeAnswerData } from "../context";
import { saveFormApi, updateFormByFormsApi } from "../hooks/api/formApi.js"; import { saveFormApi, addFormBlockApi, listFormBlockApi, updateBlockApi } from "../hooks/api/formApi.js";
import { responseDataToListBlock } from "../hooks/sundry/parseListBlock.js";
const NewForm = () => { const NewForm = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const { formId } = useParams();
const [dragElem, setDragElem] = useState(null); const [dragElem, setDragElem] = useState(null);
const [dropElem, setDropElem] = useState(null); const [dropElem, setDropElem] = useState(null);
const [cookies, _, __] = useCookies(["user"]);
const {forms, setForms} = useContext(FormsData); const {forms, setForms} = useContext(FormsData);
const {listTypeAnswer, setListTypeAnswer} = useContext(TypeAnswerData); const {listTypeAnswer, setListTypeAnswer} = useContext(TypeAnswerData);
@ -21,26 +26,26 @@ const NewForm = () => {
}; };
const [question, setQuestion] = useState(""); const [question, setQuestion] = useState("");
const [comment, setComment] = useState(""); const [comment, setComment] = useState("");
const [datetime, setDatetime] = useState("");
const [mandatory, setMandatory] = useState(false); const [mandatory, setMandatory] = useState(false);
const [optionAnswer, setOptionAnswer] = useState([]); const [optionAnswer, setOptionAnswer] = useState([]);
const [file, setFile] = useState([]); const [file, setFile] = useState([]);
const [currentTypeAnswer, setCurrentTypeAnswer] = useState(""); const [currentTypeAnswer, setCurrentTypeAnswer] = useState("");
const [currentOptionAnswer, setCurrentOptionAnswer] = useState(""); const [currentOptionAnswer, setCurrentOptionAnswer] = useState("");
const [newForm, setNewForm] = useState(location.state ? location.state.data : []); const [datetime, setDatetime] = useState("");
const [listBlock, setListBlock] = useState([]);
const [stateModal, setStateModal] = useState(false); const [stateModal, setStateModal] = useState(false);
function removeAnswerByForm(id) { function removeAnswerByForm(id) {
setNewForm([...newForm.filter(item => item.id !== id)]); setListBlock([...listBlock.filter(item => item.id !== id)]);
}; };
function cleanStates() { function cleanStates() {
setStateModal(false) setStateModal(false)
setQuestion(""); setQuestion("");
setComment(""); setComment("");
setDatetime("");
setOptionAnswer([]) setOptionAnswer([])
setFile([]); setFile([]);
setCurrentTypeAnswer(""); setCurrentTypeAnswer("");
@ -48,6 +53,21 @@ const NewForm = () => {
setMandatory(false); setMandatory(false);
}; };
useEffect(() => {
async function listFormBlock() {
const response = await listFormBlockApi(cookies.token, formId);
if (response.status === 200 && response.data) {
setListBlock(responseDataToListBlock(response.data));
}
else {
console.log(response);
}
};
listFormBlock();
}, []);
function addOptionAnswer(text) { function addOptionAnswer(text) {
setOptionAnswer([...optionAnswer, { setOptionAnswer([...optionAnswer, {
id: nextID(optionAnswer), id: nextID(optionAnswer),
@ -57,56 +77,81 @@ const NewForm = () => {
}; };
function editAnswerByForm(id) { function editAnswerByForm(id) {
const obj = newForm.find(item => item.id === id); const obj = listBlock.find(item => item.id === id);
setQuestion(obj.question); setQuestion(obj.question);
setComment(obj.comment); setComment(obj.comment);
setDatetime(obj.datetime);
setFile(obj.file); setFile(obj.file);
setCurrentTypeAnswer(obj.typeAnswer); setCurrentTypeAnswer(obj.typeAnswer);
setOptionAnswer(obj.optionAnswer); setOptionAnswer(obj.optionAnswer);
setMandatory(obj.mandatory); setMandatory(obj.mandatory);
setStateModal(id); setStateModal(obj.id);
}; };
function updateAnswerByForm() { async function updateBlock() {
setNewForm(newForm.map(item => { const data = {
id: stateModal,
question: question,
comment: comment,
file: file,
mandatory: mandatory,
optionAnswer: optionAnswer,
typeAnswer: currentTypeAnswer,
}
// const response = await updateBlockApi(cookies.token, stateModal, data);
// if (response.status === 200) {
// setListBlock(listBlock.map(item => {
// if (item.id === stateModal) {
// item = data
// }
// return item
// }))
// }
// else {
// console.log(response)
// }
setListBlock(listBlock.map(item => {
if (item.id === stateModal) { if (item.id === stateModal) {
item.question = question; item = data
item.comment = comment;
item.datetime = datetime;
item.file = file;
item.mandatory = mandatory;
item.optionAnswer = optionAnswer;
item.typeAnswer = currentTypeAnswer;
} }
return item return item
})) }))
cleanStates()
cleanStates();
}; };
function saveStates() { async function addFormBlock() {
setNewForm([...newForm, { const newBlock = {
id: nextID(newForm),
question: question, question: question,
typeAnswer: currentTypeAnswer, typeAnswer: currentTypeAnswer,
comment: comment, comment: comment,
datetime: datetime,
mandatory: mandatory, mandatory: mandatory,
optionAnswer: optionAnswer, optionAnswer: optionAnswer,
file: file file: file
}]); }
const response = await addFormBlockApi(cookies.token, formId, newBlock)
if (response.status === 200) {
setListBlock([...listBlock, newBlock]);
}
else {
console.log(response)
}
cleanStates(); cleanStates();
}; };
function updateFormByForms() { function updateFormByForms() {
updateFormByFormsApi(location.state.id, "Новая форма", newForm) updateFormByFormsApi(location.state.id, "Новая форма", listBlock)
.then((resolve, _) => { .then((resolve, _) => {
console.log(resolve); console.log(resolve);
setForms( setForms(
forms.map(item => { forms.map(item => {
if (item.id === location.state.id) { if (item.id === location.state.id) {
item.title = "Новая форма", item.title = "Новая форма",
item.questions = newForm item.questions = listBlock
} }
return item return item
}) })
@ -117,14 +162,14 @@ const NewForm = () => {
}; };
function saveForm() { function saveForm() {
saveFormApi("Новая форма", newForm) saveFormApi("Новая форма", listBlock)
.then((resolve, reject) => { .then((resolve, reject) => {
console.log(resolve); console.log(resolve);
setForms( setForms(
[...forms, { [...forms, {
id: nextID(forms), id: nextID(forms),
title: "Новая форма", title: "Новая форма",
questions: newForm, questions: listBlock,
answers: [] answers: []
}] }]
); );
@ -138,6 +183,12 @@ const NewForm = () => {
<div className={classes.main}> <div className={classes.main}>
<div className={classes.wrapper}> <div className={classes.wrapper}>
<div className={classes.header}> <div className={classes.header}>
<div className={classes.header__date}>
<div className={classes.header__date__item}>
<span>Дедлайн выполнения</span>
<input type="datetime-local" value={datetime} onChange={event => setDatetime(event.target.value)}/>
</div>
</div>
<div className={classes.header__listBtn}> <div className={classes.header__listBtn}>
<MyButton text={'Предпросмотр'} backgroundColor={'rgb(225, 225, 225)'} toggle={"modal"} target={"#previewModal"}/> <MyButton text={'Предпросмотр'} backgroundColor={'rgb(225, 225, 225)'} toggle={"modal"} target={"#previewModal"}/>
<MyButton text={'Опубликовать'} click={location.state ? updateFormByForms : saveForm}/> <MyButton text={'Опубликовать'} click={location.state ? updateFormByForms : saveForm}/>
@ -154,7 +205,7 @@ const NewForm = () => {
</div> </div>
</div> </div>
<PreviewModal newForm={newForm} listTypeAnswer={listTypeAnswer}/> <PreviewModal listBlock={listBlock} listTypeAnswer={listTypeAnswer}/>
<AnswerModal <AnswerModal
stateModal={stateModal} stateModal={stateModal}
@ -164,20 +215,18 @@ const NewForm = () => {
mandatory={mandatory} mandatory={mandatory}
optionAnswer={optionAnswer} optionAnswer={optionAnswer}
setOptionAnswer={setOptionAnswer} setOptionAnswer={setOptionAnswer}
datetime={datetime}
file={file} file={file}
currentOptionAnswer={currentOptionAnswer} currentOptionAnswer={currentOptionAnswer}
listTypeAnswer={listTypeAnswer} listTypeAnswer={listTypeAnswer}
setQuestion={setQuestion} setQuestion={setQuestion}
setComment={setComment} setComment={setComment}
setDatetime={setDatetime}
setFile={setFile} setFile={setFile}
setCurrentOptionAnswer={setCurrentOptionAnswer} setCurrentOptionAnswer={setCurrentOptionAnswer}
setMandatory={setMandatory} setMandatory={setMandatory}
cleanStates={cleanStates} cleanStates={cleanStates}
saveStates={saveStates} addFormBlock={addFormBlock}
addOptionAnswer={addOptionAnswer} addOptionAnswer={addOptionAnswer}
updateAnswerByForm={updateAnswerByForm} updateBlock={updateBlock}
setCurrentTypeAnswer={setCurrentTypeAnswer}/> setCurrentTypeAnswer={setCurrentTypeAnswer}/>
<div className={classes.content__newForm}> <div className={classes.content__newForm}>
@ -185,7 +234,7 @@ const NewForm = () => {
<h3>Новая форма</h3> <h3>Новая форма</h3>
</div> </div>
<div className={classes.content__newForm__list}> <div className={classes.content__newForm__list}>
{newForm.map((item, i) => {listBlock.map((item, i) =>
<div <div
className={classes.content__newForm__list__item} className={classes.content__newForm__list__item}
key={i} key={i}
@ -203,8 +252,8 @@ const NewForm = () => {
} }
}} }}
onDrop={() => { onDrop={() => {
const currentElem = newForm[dragElem] const currentElem = listBlock[dragElem]
const tNewForm = [...newForm] const tNewForm = [...listBlock]
if (dragElem > dropElem) { if (dragElem > dropElem) {
tNewForm.splice(dropElem, 0, currentElem) tNewForm.splice(dropElem, 0, currentElem)
tNewForm.splice(dragElem + 1, 1) tNewForm.splice(dragElem + 1, 1)
@ -213,7 +262,7 @@ const NewForm = () => {
tNewForm.splice(dropElem + 1, 0, currentElem) tNewForm.splice(dropElem + 1, 0, currentElem)
tNewForm.splice(dragElem, 1) tNewForm.splice(dragElem, 1)
} }
setNewForm(tNewForm) setListBlock(tNewForm)
}} }}
> >
<div className={classes.content__newForm__list__item__answer}> <div className={classes.content__newForm__list__item__answer}>

View File

@ -1,30 +1,60 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext, useEffect } from "react";
import classes from "../assets/styles/profile.module.scss"; import classes from "../assets/styles/profile.module.scss";
import MyButton from "../components/MyButton.jsx"; import MyButton from "../components/MyButton.jsx";
import { UserData } from "../context"; import { UserData } from "../context";
import { useCookies } from "react-cookie";
import { verifyUserApi } from "../hooks/api/enterAccountApi.js";
const Profile = () => { const Profile = () => {
const [edit, setEdit] = useState(true); const [edit, setEdit] = useState(true);
const {user, setUser} = useContext(UserData); const {user, setUser} = useContext(UserData);
const [email, setEmail] = useState(user.email); console.log(user)
const [phone, setPhone] = useState(user.phone);
const [name, setName] = useState(user.name); const [email, setEmail] = useState("");
const [surname, setSurname] = useState(user.surname); const [phone, setPhone] = useState("");
const [patronymic, setPatronymic] = useState(user.patronymic); const [login, setLogin] = useState("");
const [first_name, setFirst_name] = useState("");
const [last_name, setLast_name] = useState("");
const [cookies, _, __] = useCookies(["user"]);
useEffect(() => {
console.log(1)
async function verifyUser() {
const response = await verifyUserApi(cookies.token);
if (response) {
if (response.status === 200) {
setUser(response.data);
setEmail(response.data.email);
setPhone(response.data.phone);
setLogin(response.data.login);
setFirst_name(response.data.first_name);
setLast_name(response.data.last_name);
}
else {
console.log(response)
}
}
}
verifyUser()
}, [])
function choiceInput(key) { function choiceInput(key) {
console.log(2)
switch (key) { switch (key) {
case "email": case "email":
return {get: email, set: setEmail} return {get: email, set: setEmail}
case "phone": case "phone":
return {get: phone, set: setPhone} return {get: phone, set: setPhone}
case "name": case "login":
return {get: name, set: setName} return {get: login, set: setLogin}
case "surname": case "last_name":
return {get: surname, set: setSurname} return {get: last_name, set: setLast_name}
case "patronymic": case "first_name":
return {get: patronymic, set: setPatronymic} return {get: first_name, set: setFirst_name}
} }
} }
@ -34,11 +64,11 @@ const Profile = () => {
} }
else { else {
setUser({ setUser({
login: login,
first_name: first_name,
last_name: last_name,
email: email, email: email,
phone: phone, phone: phone,
name: name,
surname: surname,
patronymic: patronymic
}) })
setEdit(!edit) setEdit(!edit)
} }
@ -61,7 +91,7 @@ const Profile = () => {
click={() => editUser()}/> click={() => editUser()}/>
</div> </div>
<div className={classes.profile__wrapper__body}> <div className={classes.profile__wrapper__body}>
{Object.keys(user).map(key => key !== "password" ? <div className={classes.profile__wrapper__body__item}> {Object.keys(user).map(key => key !== "is_admin" ? <div className={classes.profile__wrapper__body__item}>
<div class="input-group mb-3"> <div class="input-group mb-3">
<span class="input-group-text">{key}</span> <span class="input-group-text">{key}</span>
<input <input

View File

@ -1,26 +1,67 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { useCookies } from "react-cookie";
import classes from "../assets/styles/viewForm.module.scss"; import classes from "../assets/styles/viewForm.module.scss";
import { FormsData, TypeAnswerData } from "../context"; import { FormsData, TypeAnswerData } from "../context";
import GeneratingFormFields from "../components/GeneratingFormFields.jsx"; import GeneratingFormFields from "../components/GeneratingFormFields.jsx";
import MyButton from "../components/MyButton.jsx"; import MyButton from "../components/MyButton.jsx";
import { saveAnswersApi } from "../hooks/api/formApi.js"; import { listFormBlockApi } from "../hooks/api/formApi.js";
import { listFormsApi } from "../hooks/api/listFormsApi.js";
import { responseDataToListBlock } from "../hooks/sundry/parseListBlock.js";
const ViewForm = () => { const ViewForm = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { formId } = useParams(); const { formId } = useParams();
const {forms, setForms} = useContext(FormsData); const {forms, setForms} = useContext(FormsData);
const {listTypeAnswer, setListTypeAnswer} = useContext(TypeAnswerData); const {listTypeAnswer, setListTypeAnswer} = useContext(TypeAnswerData);
const [cookies, _, __] = useCookies(["user"]);
const [questions, setQuestions] = useState([]);
const [answers, setAnswers] = useState([]);
const [title, setTitle] = useState("");
function newForm() {
return forms.find(item => item.id === Number(formId)) useEffect(() => {
async function getForm() {
const responseForms = await listFormsApi(cookies.token);
const responseBlocks = await listFormBlockApi(cookies.token, formId);
if (responseBlocks.status === 200 && responseForms.status === 200 && responseBlocks.data) {
// let responseToNewForm = {};
// const result = {
// title: responseForms.data.find(item => item.id === formId).title,
// questions: [],
// answers: []
// }
// for (let item of responseBlocks.data) {
// responseToNewForm["id"] = item.id;
// for (let block of item.data) {
// responseToNewForm[block.Key] = block.Value
// };
// result.questions.push(responseToNewForm);
// result.answers.push(
// {id: item.id, answer: []}
// )
// responseToNewForm = {};
// };
const listBlocks = responseDataToListBlock(responseBlocks.data);
setQuestions(listBlocks)
setAnswers(listBlocks.map(item => (
{id: item.id, answer: []}
)))
setTitle(responseForms.data.find(item => item.id === formId).title)
}
else {
console.log(responseForms)
console.log(responseBlocks)
}
}; };
const [answers, setAnswers] = useState( getForm()
newForm() ? newForm().questions.map(item => ( }, []);
{id: item.id, answer: []}
)) : []
);
function updateAnswersForm(value, id) { function updateAnswersForm(value, id) {
setAnswers( setAnswers(
@ -34,30 +75,32 @@ const ViewForm = () => {
}; };
function saveAnswers() { function saveAnswers() {
saveAnswersApi(formId, answers) // saveAnswersApi(formId, answers)
.then((resolve, _) => { // .then((resolve, _) => {
console.log(resolve) // console.log(resolve)
// setAnswers([]);
// navigate("/");
// })
// .catch((error) => console.log(error));
setAnswers([]); setAnswers([]);
navigate("/"); navigate("/forms");
})
.catch((error) => console.log(error));
} }
return ( return (
<div className={classes.main}> <div className={classes.main}>
{newForm() ? {questions ?
<div className={classes.wrapper}> <div className={classes.wrapper}>
<div className={classes.form}> <div className={classes.form}>
<div className={classes.form__header}> <div className={classes.form__header}>
<div className={classes.form__header__title}> <div className={classes.form__header__title}>
<span>{newForm().title}</span> <span>{title}</span>
</div> </div>
<div className={classes.form__header__id}> <div className={classes.form__header__id}>
<span>#{formId}</span> <span>#{formId}</span>
</div> </div>
</div> </div>
<div className={classes.form__content}> <div className={classes.form__content}>
<GeneratingFormFields newForm={newForm().questions} listTypeAnswer={listTypeAnswer} answers={answers} updateAnswersForm={updateAnswersForm}/> <GeneratingFormFields listBlock={questions} listTypeAnswer={listTypeAnswer} answers={answers} updateAnswersForm={updateAnswersForm}/>
</div> </div>
</div> </div>
<div className={classes.footer}> <div className={classes.footer}>

View File

@ -21,7 +21,7 @@ const router = createBrowserRouter([
element: <Forms/>, element: <Forms/>,
}, },
{ {
path: "/forms/edit", path: "/forms/:formId/edit",
element: <NewForm/> element: <NewForm/>
}, },
{ {