create ViewForm page
This commit is contained in:
parent
16a82736a4
commit
90aec8fad0
22
src/App.jsx
22
src/App.jsx
@ -1,18 +1,37 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Outlet, useNavigate } from "react-router-dom";
|
import { Outlet, useNavigate } from "react-router-dom";
|
||||||
import { FormsData, UserData } from "./context";
|
import { FormsData, UserData, TypeAnswerData } from "./context";
|
||||||
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';
|
||||||
|
import InputText from "./components/typeAnswer/InputText.jsx";
|
||||||
|
import TextArea from "./components/typeAnswer/TextArea.jsx";
|
||||||
|
import YesNo from "./components/typeAnswer/YesNo.jsx"
|
||||||
|
import InputDate from "./components/typeAnswer/InputDate.jsx";
|
||||||
|
import InputMultipleRadio from "./components/typeAnswer/InputMultipleRadio.jsx";
|
||||||
|
import InputRadio from "./components/typeAnswer/InputRadio.jsx";
|
||||||
|
import DropDownList from "./components/typeAnswer/DropDownList.jsx";
|
||||||
|
import InputFile from "./components/typeAnswer/InputFile.jsx";
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [forms, setForms] = useState([]);
|
const [forms, setForms] = useState([]);
|
||||||
const [user, setUser] = useState(false);
|
const [user, setUser] = useState(false);
|
||||||
|
const [listTypeAnswer, setListTypeAnswer] = useState([
|
||||||
|
{id: 1, text: 'Краткий ответ', typeTag: InputText},
|
||||||
|
{id: 2, text: 'Расширенный ответ', typeTag: TextArea},
|
||||||
|
{id: 3, text: 'Выбор из вариантов', typeTag: InputRadio},
|
||||||
|
{id: 4, text: 'Множественный выбор', typeTag: InputMultipleRadio},
|
||||||
|
{id: 5, text: 'Выпадающий список', typeTag: DropDownList},
|
||||||
|
{id: 6, text: 'Да/Нет', typeTag: YesNo},
|
||||||
|
{id: 7, text: 'Файл', typeTag: InputFile},
|
||||||
|
{id: 8, text: 'Дата', typeTag: InputDate}
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UserData.Provider value={{ user, setUser }}>
|
<UserData.Provider value={{ user, setUser }}>
|
||||||
<FormsData.Provider value={{ forms, setForms }}>
|
<FormsData.Provider value={{ forms, setForms }}>
|
||||||
|
<TypeAnswerData.Provider value={{ listTypeAnswer, setListTypeAnswer }}>
|
||||||
<div className={classes.main}>
|
<div className={classes.main}>
|
||||||
<div className={classes.container}>
|
<div className={classes.container}>
|
||||||
<div className={classes.header}>
|
<div className={classes.header}>
|
||||||
@ -23,6 +42,7 @@ const App = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</TypeAnswerData.Provider>
|
||||||
</FormsData.Provider>
|
</FormsData.Provider>
|
||||||
</UserData.Provider>
|
</UserData.Provider>
|
||||||
)
|
)
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
height: 25%;
|
height: 25%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: "Montserrat", sans-serif;
|
font-family: "Montserrat", sans-serif;
|
||||||
|
position: relative;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba(240, 240, 240, 0.8);
|
background-color: rgba(240, 240, 240, 0.8);
|
||||||
}
|
}
|
||||||
@ -72,6 +73,13 @@
|
|||||||
width: 33.3%;
|
width: 33.3%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
i {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 15px;
|
||||||
|
right: 30px;
|
||||||
|
top: calc(50% - 7px);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
19
src/assets/styles/generatingFormFields.module.scss
Normal file
19
src/assets/styles/generatingFormFields.module.scss
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.item {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-top: 1px solid rgb(200, 200, 200);
|
||||||
|
&__question {
|
||||||
|
&__text {
|
||||||
|
font-size: 15px;
|
||||||
|
font-family: "Montserrat", sans-serif;
|
||||||
|
}
|
||||||
|
&__comment {
|
||||||
|
font-size: 11px;
|
||||||
|
font-family: "Montserrat", sans-serif;
|
||||||
|
font-style: italic;
|
||||||
|
color: rgb(200, 200, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__answer {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
27
src/assets/styles/viewForm.module.scss
Normal file
27
src/assets/styles/viewForm.module.scss
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
.main {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
width: 70%;
|
||||||
|
min-height: 80%;
|
||||||
|
box-shadow: 0 0 5px 1px rgb(200, 200, 200);
|
||||||
|
&__header {
|
||||||
|
|
||||||
|
}
|
||||||
|
&__content {
|
||||||
|
|
||||||
|
}
|
||||||
|
&__footer {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
22
src/components/GeneratingFormFields.jsx
Normal file
22
src/components/GeneratingFormFields.jsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import classes from "../assets/styles/generatingFormFields.module.scss";
|
||||||
|
|
||||||
|
const GeneratingFormFields = ({newForm, listTypeAnswer}) => {
|
||||||
|
return (
|
||||||
|
newForm.map((item, i) =>
|
||||||
|
<div className={classes.item} key={i}>
|
||||||
|
<div className={classes.item__question}>
|
||||||
|
<p className={classes.item__question__text}>{i + 1}) {item.question}</p>
|
||||||
|
<p className={classes.item__question__comment}>{item.comment}</p>
|
||||||
|
</div>
|
||||||
|
<div className={classes.item__answer}>
|
||||||
|
{
|
||||||
|
listTypeAnswer.find(type => type.id === item.typeAnswer).typeTag({postfix: i, answers: item.optionAnswer})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GeneratingFormFields;
|
@ -1,10 +1,8 @@
|
|||||||
import React from "react";
|
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";
|
||||||
|
|
||||||
const PreviewModal = ({newForm, listTypeAnswer}) => {
|
const PreviewModal = ({newForm, listTypeAnswer}) => {
|
||||||
// const [file, setFile] = useState('');
|
|
||||||
// const [value, setValue] = useState('');
|
|
||||||
|
|
||||||
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}>
|
||||||
@ -14,19 +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}>
|
||||||
{newForm.map((item, i) =>
|
<GeneratingFormFields newForm={newForm} listTypeAnswer={listTypeAnswer}/>
|
||||||
<div className={classes.myModal__dialog__content__body__item} key={i}>
|
|
||||||
<div className={classes.myModal__dialog__content__body__item__question}>
|
|
||||||
<p className={classes.myModal__dialog__content__body__item__question__text}>{i + 1}) {item.question}</p>
|
|
||||||
<p className={classes.myModal__dialog__content__body__item__question__comment}>{item.comment}</p>
|
|
||||||
</div>
|
|
||||||
<div className={classes.myModal__dialog__content__body__item__answer}>
|
|
||||||
{
|
|
||||||
listTypeAnswer.find(type => type.id === item.typeAnswer).typeTag({postfix: i, answers: item.optionAnswer})
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer" className={classes.myModal__dialog__content__footer}>
|
<div class="modal-footer" className={classes.myModal__dialog__content__footer}>
|
||||||
|
|
||||||
|
@ -3,3 +3,5 @@ import { createContext } from "react";
|
|||||||
export const FormsData = createContext([]);
|
export const FormsData = createContext([]);
|
||||||
|
|
||||||
export const UserData = createContext(false);
|
export const UserData = createContext(false);
|
||||||
|
|
||||||
|
export const TypeAnswerData = createContext();
|
@ -8,7 +8,7 @@ import { FormsData } from "../context";
|
|||||||
const Forms = () => {
|
const Forms = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const {forms, setForms} = useContext(FormsData);
|
const {forms, setForms} = useContext(FormsData);
|
||||||
const [stateLoading, setStateLoading] = useState(false)
|
const [stateLoading, setStateLoading] = useState(false);
|
||||||
|
|
||||||
const response = ms => {
|
const response = ms => {
|
||||||
return new Promise(r => setTimeout(() => r('response end'), ms))
|
return new Promise(r => setTimeout(() => r('response end'), ms))
|
||||||
@ -27,7 +27,7 @@ const Forms = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function editForm(item) {
|
function editForm(item) {
|
||||||
navigate("/new", {
|
navigate("/forms/edit", {
|
||||||
state: {
|
state: {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
data: item.listAnswer
|
data: item.listAnswer
|
||||||
@ -58,6 +58,11 @@ const Forms = () => {
|
|||||||
<div className={classes.listForms__forms__item__title} onClick={() => editForm(item)}>{item.title}</div>
|
<div className={classes.listForms__forms__item__title} onClick={() => editForm(item)}>{item.title}</div>
|
||||||
<div className={classes.listForms__forms__item__answers}>{item.answers}</div>
|
<div className={classes.listForms__forms__item__answers}>{item.answers}</div>
|
||||||
<div className={classes.listForms__forms__item__update}>{item.update}</div>
|
<div className={classes.listForms__forms__item__update}>{item.update}</div>
|
||||||
|
<i class="fa-solid fa-ellipsis-vertical" id="action" data-bs-toggle="dropdown"></i>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="action">
|
||||||
|
<li><a class="dropdown-item" onClick={() => navigate(`/forms/${item.id}/`)}>Открыть</a></li>
|
||||||
|
<li><a class="dropdown-item" onClick={() => navigator.clipboard.writeText(`http://localhost:3000/forms/${item.id}/`)}>Скопировать ссылку</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,15 +4,7 @@ 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 } from "../context";
|
import { FormsData, TypeAnswerData } from "../context";
|
||||||
import InputText from "../components/typeAnswer/InputText.jsx"
|
|
||||||
import TextArea from "../components/typeAnswer/TextArea.jsx";
|
|
||||||
import YesNo from "../components/typeAnswer/YesNo.jsx"
|
|
||||||
import InputDate from "../components/typeAnswer/InputDate.jsx";
|
|
||||||
import InputMultipleRadio from "../components/typeAnswer/InputMultipleRadio.jsx";
|
|
||||||
import InputRadio from "../components/typeAnswer/InputRadio.jsx";
|
|
||||||
import DropDownList from "../components/typeAnswer/DropDownList.jsx";
|
|
||||||
import InputFile from "../components/typeAnswer/InputFile.jsx";
|
|
||||||
|
|
||||||
const NewForm = () => {
|
const NewForm = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -21,6 +13,7 @@ const NewForm = () => {
|
|||||||
const [dropElem, setDropElem] = useState(null);
|
const [dropElem, setDropElem] = useState(null);
|
||||||
|
|
||||||
const {forms, setForms} = useContext(FormsData);
|
const {forms, setForms} = useContext(FormsData);
|
||||||
|
const {listTypeAnswer, setListTypeAnswer} = useContext(TypeAnswerData);
|
||||||
|
|
||||||
const nextID = (list) => {
|
const nextID = (list) => {
|
||||||
return list.length ? list.at(-1).id + 1 : 1
|
return list.length ? list.at(-1).id + 1 : 1
|
||||||
@ -38,17 +31,6 @@ const NewForm = () => {
|
|||||||
|
|
||||||
const [stateModal, setStateModal] = useState(false)
|
const [stateModal, setStateModal] = useState(false)
|
||||||
|
|
||||||
const [listTypeAnswer, setListTypeAnswer] = useState([
|
|
||||||
{id: 1, text: 'Краткий ответ', typeTag: InputText},
|
|
||||||
{id: 2, text: 'Расширенный ответ', typeTag: TextArea},
|
|
||||||
{id: 3, text: 'Выбор из вариантов', typeTag: InputRadio},
|
|
||||||
{id: 4, text: 'Множественный выбор', typeTag: InputMultipleRadio},
|
|
||||||
{id: 5, text: 'Выпадающий список', typeTag: DropDownList},
|
|
||||||
{id: 6, text: 'Да/Нет', typeTag: YesNo},
|
|
||||||
{id: 7, text: 'Файл', typeTag: InputFile},
|
|
||||||
{id: 8, text: 'Дата', typeTag: InputDate}
|
|
||||||
]);
|
|
||||||
|
|
||||||
function removeAnswerByForm(id) {
|
function removeAnswerByForm(id) {
|
||||||
setNewForm([...newForm.filter(item => item.id !== id)]);
|
setNewForm([...newForm.filter(item => item.id !== id)]);
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,42 @@ 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("");
|
const [email, setEmail] = useState(user.email);
|
||||||
const [phone, setPhone] = useState("");
|
const [phone, setPhone] = useState(user.phone);
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState(user.name);
|
||||||
const [surname, setSurname] = useState("");
|
const [surname, setSurname] = useState(user.surname);
|
||||||
const [patronymic, setPatronymic] = useState("");
|
const [patronymic, setPatronymic] = useState(user.patronymic);
|
||||||
|
|
||||||
|
function choiceInput(key) {
|
||||||
|
switch (key) {
|
||||||
|
case "email":
|
||||||
|
return {get: email, set: setEmail}
|
||||||
|
case "phone":
|
||||||
|
return {get: phone, set: setPhone}
|
||||||
|
case "name":
|
||||||
|
return {get: name, set: setName}
|
||||||
|
case "surname":
|
||||||
|
return {get: surname, set: setSurname}
|
||||||
|
case "patronymic":
|
||||||
|
return {get: patronymic, set: setPatronymic}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function editUser() {
|
||||||
|
if (edit) {
|
||||||
|
setEdit(!edit)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setUser({
|
||||||
|
email: email,
|
||||||
|
phone: phone,
|
||||||
|
name: name,
|
||||||
|
surname: surname,
|
||||||
|
patronymic: patronymic
|
||||||
|
})
|
||||||
|
setEdit(!edit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.main}>
|
<div className={classes.main}>
|
||||||
@ -27,7 +58,7 @@ const Profile = () => {
|
|||||||
<span>Сохранить <i class="fa-solid fa-floppy-disk"></i></span>
|
<span>Сохранить <i class="fa-solid fa-floppy-disk"></i></span>
|
||||||
}
|
}
|
||||||
backgroundColor={edit ? "rgb(200, 200, 200)" : ""}
|
backgroundColor={edit ? "rgb(200, 200, 200)" : ""}
|
||||||
click={() => setEdit(!edit)}/>
|
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 !== "password" ? <div className={classes.profile__wrapper__body__item}>
|
||||||
@ -36,9 +67,10 @@ const Profile = () => {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control shadow-none"
|
class="form-control shadow-none"
|
||||||
value={user[key]}
|
value={choiceInput(key).get}
|
||||||
pattern={key === "email" ? "+[7-8]{1}[0-9]{3} [0-9]{3}-[0-9]{2}-[0-9]{2}" : ""}
|
pattern={key === "email" ? "+[7-8]{1}[0-9]{3} [0-9]{3}-[0-9]{2}-[0-9]{2}" : ""}
|
||||||
disabled={edit}
|
disabled={edit}
|
||||||
|
onChange={(e) => choiceInput(key).set(e.target.value)}
|
||||||
required={key !== "patronymic" ? true : false}/>
|
required={key !== "patronymic" ? true : false}/>
|
||||||
</div>
|
</div>
|
||||||
</div> : <div></div>)}
|
</div> : <div></div>)}
|
||||||
|
41
src/pages/ViewForm.jsx
Normal file
41
src/pages/ViewForm.jsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React, { useState, useContext } from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import classes from "../assets/styles/viewForm.module.scss";
|
||||||
|
import { FormsData, TypeAnswerData } from "../context";
|
||||||
|
import GeneratingFormFields from "../components/GeneratingFormFields.jsx";
|
||||||
|
|
||||||
|
const ViewForm = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { formId } = useParams();
|
||||||
|
const {forms, setForms} = useContext(FormsData);
|
||||||
|
const {listTypeAnswer, setListTypeAnswer} = useContext(TypeAnswerData);
|
||||||
|
|
||||||
|
function newForm() {
|
||||||
|
const searchForm = forms.find(item => item.id === Number(formId))
|
||||||
|
|
||||||
|
if (searchForm) {
|
||||||
|
return searchForm.listAnswer
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.main}>
|
||||||
|
<div className={classes.wrapper}>
|
||||||
|
<div className={classes.form}>
|
||||||
|
<div className={classes.form__header}>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className={classes.form__content}>
|
||||||
|
<GeneratingFormFields newForm={newForm()} listTypeAnswer={listTypeAnswer}/>
|
||||||
|
</div>
|
||||||
|
<div className={classes.form__footer}>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ViewForm;
|
@ -6,6 +6,7 @@ import NewForm from '../pages/NewForm.jsx';
|
|||||||
import Home from "../pages/Home.jsx";
|
import Home from "../pages/Home.jsx";
|
||||||
import App from "../App.jsx";
|
import App from "../App.jsx";
|
||||||
import Profile from "../pages/Profile.jsx";
|
import Profile from "../pages/Profile.jsx";
|
||||||
|
import ViewForm from "../pages/ViewForm.jsx";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@ -30,6 +31,10 @@ const router = createBrowserRouter([
|
|||||||
{
|
{
|
||||||
path: '/profile',
|
path: '/profile',
|
||||||
element: <Profile/>
|
element: <Profile/>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/forms/:formId',
|
||||||
|
element: <ViewForm/>
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user