diff --git a/package.json b/package.json index 73909f9..1342aba 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,10 @@ "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", + "@popperjs/core": "^2.11.8", + "bootstrap": "^5.3.3", "react": "^18.2.0", + "react-bootstrap": "^2.10.1", "react-dom": "^18.2.0", "react-router-dom": "^6.22.1" } diff --git a/public/index.html b/public/index.html index c8712f8..03180da 100644 --- a/public/index.html +++ b/public/index.html @@ -4,8 +4,11 @@ Document + + -
+
+ \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 7c4410e..0a179bc 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,21 +1,31 @@ -import React from "react"; +import React, { useState } from "react"; import { RouterProvider } from "react-router-dom"; +import {FormsData} from "./context"; import router from "./router/router" import classes from "./assets/styles/app.module.scss" import NavBar from "./components/NavBar.jsx"; +import 'bootstrap/dist/css/bootstrap.min.css'; const App = () => { + const [forms, setForms] = useState([]) + return ( -
-
-
- -
-
- + +
+
+
+ +
+
+ +
-
+ + ) } diff --git a/src/assets/styles/app.module.scss b/src/assets/styles/app.module.scss index b576884..5491749 100644 --- a/src/assets/styles/app.module.scss +++ b/src/assets/styles/app.module.scss @@ -1,4 +1,5 @@ @import url('./base.css'); +@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap'); .main { width: 100%; @@ -13,7 +14,7 @@ .header { background-color: rgb(240, 240, 240); width: 100%; - height: 50px; + height: 10%; box-shadow: 0 0 5px 1px rgb(210, 210, 210); } diff --git a/src/assets/styles/components/myButton.module.scss b/src/assets/styles/components/myButton.module.scss index 8d624d8..47db7cc 100644 --- a/src/assets/styles/components/myButton.module.scss +++ b/src/assets/styles/components/myButton.module.scss @@ -1,6 +1,12 @@ .main { + height: 100%; button { font-size: 15px; - padding: 10%; + height: 100%; + padding: .40em 1.2rem; + color: white; + background-color: rgb(150, 209, 158); + border-radius: 5px; + font-family: "Montserrat", sans-serif; } } \ No newline at end of file diff --git a/src/assets/styles/components/myInput.module.scss b/src/assets/styles/components/myInput.module.scss index 7082a37..df57d08 100644 --- a/src/assets/styles/components/myInput.module.scss +++ b/src/assets/styles/components/myInput.module.scss @@ -1,5 +1,12 @@ .main { + height: 100%; input { - + border: 1px solid rgb(180, 180, 180); + height: 100%; + border-radius: 5px; + font-family: "Montserrat", sans-serif; + padding: 0 3px; + color: rgb(40, 40, 40); + font-size: 15px; } } \ No newline at end of file diff --git a/src/assets/styles/components/myModal.module.scss b/src/assets/styles/components/myModal.module.scss new file mode 100644 index 0000000..131684f --- /dev/null +++ b/src/assets/styles/components/myModal.module.scss @@ -0,0 +1,71 @@ +.myModal { + &__dialog { + &__content { + &__header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 3%; + h5 { + display: block; + } + select { + display: block; + font-size: 15px; + font-family: "Montserrat", sans-serif; + border: 1px solid rgb(200, 200, 200); + padding: 5px; + } + } + &__body { + &__answer { + &__title { + display: block; + font-size: 15px; + font-family: "Montserrat", sans-serif; + } + &__text { + display: block; + font-family: "Montserrat", sans-serif; + border: 1px solid rgb(200, 200, 200); + width: 100%; + padding: 0 1%; + } + &__file { + margin-top: 10px; + } + } + &__comment { + margin-top: 5%; + &__title { + display: block; + font-size: 15px; + font-family: "Montserrat", sans-serif; + } + &__text { + display: block; + font-family: "Montserrat", sans-serif; + border: 1px solid rgb(200, 200, 200); + width: 100%; + padding: 0 1%; + } + } + &__time { + margin-top: 5%; + span { + display: block; + font-size: 15px; + font-family: "Montserrat", sans-serif; + } + input { + display: block; + font-size: 15px; + font-family: "Montserrat", sans-serif; + border: 1px solid rgb(200, 200, 200); + padding: 1%; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/assets/styles/forms.module.scss b/src/assets/styles/forms.module.scss index 38f1a42..f8fee76 100644 --- a/src/assets/styles/forms.module.scss +++ b/src/assets/styles/forms.module.scss @@ -7,17 +7,71 @@ .wrapper { width: 100%; height: 100%; - border: 1px solid red; } .panel { width: 100%; - height: 30px; + height: 10%; display: flex; justify-content: space-between; align-items: center; + padding: 0 5%; } .listForms { - + box-shadow: 0 0 5px 1px rgb(200, 200, 200); + border-radius: 5px; + margin-top: 5%; + height: 70%; + width: 100%; + &__columns { + display: flex; + justify-content: space-around; + align-items: center; + height: 15%; + width: 100%; + border-bottom: 1px solid rgb(220, 220, 220); + &__item { + font-size: 15px; + font-family: "Montserrat", sans-serif; + } + } + &__forms { + width: 100%; + height: 85%; + overflow-y: auto; + &::-webkit-scrollbar { + width: 7px; + } + &::-webkit-scrollbar-thumb { + background-color: rgb(200, 200, 200); + } + &__item { + display: flex; + justify-content: space-around; + align-items: center; + height: 25%; + width: 100%; + font-family: "Montserrat", sans-serif; + &:hover { + background-color: rgba(240, 240, 240, 0.8); + } + &__title { + width: 33.3%; + text-align: center; + cursor: pointer; + &:hover { + text-decoration: underline; + } + } + &__answers { + width: 33.3%; + text-align: center; + } + &__update { + width: 33.3%; + text-align: center; + } + } + } } \ No newline at end of file diff --git a/src/assets/styles/newForm.module.scss b/src/assets/styles/newForm.module.scss new file mode 100644 index 0000000..a9c44df --- /dev/null +++ b/src/assets/styles/newForm.module.scss @@ -0,0 +1,131 @@ +.main { + width: 100%; + height: 100%; + padding: 4% 8%; +} + +.wrapper { + width: 100%; + height: 100%; +} + +.header { + text-align: right; + display: flex; + justify-content: end; + width: 100%; + height: 8%; + &__listBtn { + display: flex; + justify-content: space-between; + width: 20%; + } +} + +.content { + margin-top: 3%; + height: 85%; + width: 100%; + display: flex; + justify-content: space-between; + &__listQuestion { + height: 100%; + width: 25%; + &__list { + height: 100%; + width: 100%; + &__item { + height: 12.5%; + width: 100%; + display: flex; + align-items: center; + padding: 0 5px; + cursor: pointer; + + &:hover { + background-color: rgba(230, 230, 230, 0.6); + } + span { + display: block; + font-size: 15px; + font-family: "Montserrat", sans-serif; + } + } + &__item:not(:last-child) { + border-bottom: 1px solid rgb(220, 220, 220); + } + } + } + &__newForm { + box-shadow: 0 0 5px 1px rgb(220, 220, 220); + height: 100%; + width: 75%; + &__title { + padding: 1%; + width: 100%; + height: 10%; + h3 { + font-size: 20px; + font-family: "Montserrat", sans-serif; + color: rgb(80, 80, 80); + } + } + + &__list { + overflow-y: auto; + width: 100%; + height: 90%; + &::-webkit-scrollbar { + width: 7px; + } + &::-webkit-scrollbar-thumb { + background-color: rgb(200, 200, 200); + } + &__item { + padding: 1%; + width: 100%; + height: 25%; + border-bottom: 1px solid rgb(220, 220, 220); + display: flex; + justify-content: space-between; + align-items: center; + &:hover { + background-color: rgba(230, 230, 230, 0.6); + cursor: pointer; + } + &__answer { + width: 90%; + span { + display: block; + } + span:first-child { + font-size: 15px; + font-family: "Montserrat", sans-serif; + color: rgb(80, 80, 80); + } + span:last-child { + font-size: 12px; + font-family: "Montserrat", sans-serif; + color: rgb(180, 180, 180); + } + } + &__btn { + width: 10%; + display: flex; + justify-content: space-around; + align-items: center; + i { + display: block; + } + i:first-child:hover { + color: rgb(42, 64, 187); + } + i:last-child:hover { + color: rgb(199, 73, 73); + } + } + } + } + } +} + diff --git a/src/components/MyButton.jsx b/src/components/MyButton.jsx index 7ca2e0b..527a105 100644 --- a/src/components/MyButton.jsx +++ b/src/components/MyButton.jsx @@ -1,10 +1,17 @@ import React from "react"; import classes from "../assets/styles/components/myButton.module.scss" -const MyButton = () => { +const MyButton = (props) => { + return (
- +
) } diff --git a/src/components/MyInput.jsx b/src/components/MyInput.jsx index 4aef7f8..d5e40a8 100644 --- a/src/components/MyInput.jsx +++ b/src/components/MyInput.jsx @@ -1,10 +1,10 @@ import React from "react"; import classes from "../assets/styles/components/myInput.module.scss" -const MyInput = () => { +const MyInput = (props) => { return (
- +
) } diff --git a/src/components/MyModal.jsx b/src/components/MyModal.jsx new file mode 100644 index 0000000..9badffb --- /dev/null +++ b/src/components/MyModal.jsx @@ -0,0 +1,67 @@ +import React from "react"; +import classes from "../assets/styles/components/myModal.module.scss"; + +const MyModal = ({ + cleanStates, + currentTypeAnswer, + updateAnswerByForm, + stateModal, + saveStates, + answer, + file, + listTypeAnswer, + comment, + datetime, + setAnswer, + setComment, + setDatetime, + setCurrentTypeAnswer, + setFile + }) => { + + + return ( + + ) +} + +export default MyModal; \ No newline at end of file diff --git a/src/components/NavBar.jsx b/src/components/NavBar.jsx index ff93300..1c6386b 100644 --- a/src/components/NavBar.jsx +++ b/src/components/NavBar.jsx @@ -1,13 +1,15 @@ import React from "react"; +import { Link } from "react-router-dom"; import classes from "../assets/styles/components/navbar.module.scss" const NavBar = () => { return ( - +
- + {/* New Form + Forms */}
- +
) } diff --git a/src/context/index.js b/src/context/index.js new file mode 100644 index 0000000..8e03303 --- /dev/null +++ b/src/context/index.js @@ -0,0 +1,3 @@ +import { createContext } from "react"; + +export const FormsData = createContext([]) \ No newline at end of file diff --git a/src/hooks/dragDrop.js b/src/hooks/dragDrop.js new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/Forms.jsx b/src/pages/Forms.jsx index 26a9a5a..1d65859 100644 --- a/src/pages/Forms.jsx +++ b/src/pages/Forms.jsx @@ -1,17 +1,67 @@ -import React from "react"; +import React, { useState, useContext } from "react"; +import { useNavigate } from 'react-router-dom'; import classes from "../assets/styles/forms.module.scss" import MyButton from "../components/MyButton.jsx"; import MyInput from "../components/MyInput.jsx"; +import { FormsData } from "../context"; const Forms = () => { + const navigate = useNavigate() + const {forms, setForms} = useContext(FormsData); + const [stateLoading, setStateLoading] = useState(false) + + const response = ms => { + return new Promise(r => setTimeout(() => r('response end'), ms)) + } + + function createForm() { + setStateLoading(true); + // Эмуляция пост запроса + response(1000) + .then((r) => { + console.log(r); + setStateLoading(false); + navigate("/new"); + } + ) + } + + function editForm(item) { + navigate("/new", { + state: { + id: item.id, + data: item.listAnswer + } + }); + } + return (
- - + + + Загрузка... +
: 'Создать' + }/> +
+
+
+
Название
+
Ответы
+
Изменения
+
+
+ {forms.map((item, i) => +
+
editForm(item)}>{item.title}
+
{item.answers}
+
{item.update}
+
+ )} +
-
) diff --git a/src/pages/NewForm.jsx b/src/pages/NewForm.jsx index 988bf31..459535f 100644 --- a/src/pages/NewForm.jsx +++ b/src/pages/NewForm.jsx @@ -1,9 +1,213 @@ -import React from "react"; +import React, { useState, useContext } from "react"; +import { useNavigate, useLocation } from 'react-router-dom'; +import classes from "../assets/styles/newForm.module.scss"; +import MyButton from "../components/MyButton.jsx"; +import MyModal from "../components/MyModal.jsx"; +import { FormsData } from "../context"; const NewForm = () => { + const navigate = useNavigate(); + const location = useLocation(); + const [dragElem, setDragElem] = useState(null); + const [dropElem, setDropElem] = useState(null); + + const {forms, setForms} = useContext(FormsData); + + const nextID = (list) => { + return list.length ? list.at(-1).id + 1 : 1 + }; + const [answer, setAnswer] = useState(""); + const [comment, setComment] = useState(""); + const [datetime, setDatetime] = useState(""); + const [file, setFile] = useState([]); + const [currentTypeAnswer, setCurrentTypeAnswer] = useState(""); + + const [newForm, setNewForm] = useState(location.state ? location.state.data : []); + + const [stateModal, setStateModal] = useState(false) + + const [listTypeAnswer, setListTypeAnswer] = useState([ + {id: 1, text: 'Краткий ответ'}, + {id: 2, text: 'Расширенный ответ'}, + {id: 3, text: 'Выбор из вариантов'}, + {id: 4, text: 'Множественный выбор'}, + {id: 5, text: 'Выпадающий список'}, + {id: 6, text: 'Да/Нет'}, + {id: 7, text: 'Файл'}, + {id: 8, text: 'Дата'} + ]); + + function removeAnswerByForm(id) { + setNewForm([...newForm.filter(item => item.id !== id)]); + } + + function cleanStates() { + setStateModal(false) + setAnswer(""); + setComment(""); + setDatetime(""); + setFile(""); + setCurrentTypeAnswer(""); + } + + function editAnswerByForm(id) { + const obj = newForm.find(item => item.id === id); + setAnswer(obj.answer); + setComment(obj.comment); + setDatetime(obj.datetime); + setFile(obj.file); + setCurrentTypeAnswer(obj.typeAnswer); + setStateModal(id); + } + + function updateAnswerByForm() { + console.log(currentTypeAnswer) + setNewForm(newForm.map(item => { + if (item.id === stateModal) { + item.answer = answer; + item.comment = comment; + item.datetime = datetime; + item.file = file; + item.typeAnswer = currentTypeAnswer; + } + return item + })) + cleanStates() + } + + function saveStates() { + setNewForm([...newForm, { + id: nextID(newForm), + answer: answer, + typeAnswer: currentTypeAnswer, + comment: comment, + datetime: datetime, + file: file + }]); + cleanStates(); + } + + function updateFormByForms() { + setForms( + forms.map(item => { + if (item.id === location.state.id) { + item.title = 'Новая форма', + item.answers = 'Без изменений', + item.update = '01/01/24', + item.listAnswer = newForm + } + return item + }) + ) + cleanStates(); + navigate("/forms"); + } + + function saveForm() { + setForms( + [...forms, { + id: nextID(forms), + title: 'Новая форма', + answers: 'Без изменений', + update: '01/01/24', + listAnswer: newForm + }] + ); + cleanStates(); + navigate("/forms"); + } + return ( -
- NEW +
+
+
+
+ + } click={() => console.log(newForm)} backgroundColor={'rgb(225, 225, 225)'}/> +
+
+
+
+
+ {listTypeAnswer.map((item, i) => +
setCurrentTypeAnswer(item.id)} key={i}> + {item.text} +
+ )} +
+
+ + + +
+
+

Новая форма

+
+
+ {newForm.map((item, i) => +
{event.preventDefault()}} + onDragStart={(event) => { + if (event.target.id) { + setDragElem(Number(event.target.id)) + } + }} + onDragEnter={(event) => { + if (event.target.id) { + setDropElem(Number(event.target.id)) + } + }} + onDrop={(event) => { + const currentElem = newForm[dragElem] + const tNewForm = [...newForm] + if (dragElem > dropElem) { + tNewForm.splice(dropElem, 0, currentElem) + tNewForm.splice(dragElem + 1, 1) + } + else { + tNewForm.splice(dropElem + 1, 0, currentElem) + tNewForm.splice(dragElem, 1) + } + setNewForm(tNewForm) + }} + > +
+ {item.answer} + {listTypeAnswer.map(typeItem => { + if (typeItem.id === item.typeAnswer) { + return typeItem.text + } + })} +
+
+ {editAnswerByForm(item.id)}}> + removeAnswerByForm(item.id)}> +
+
+ )} +
+
+
+
) }