Compare commits
6 Commits
750b571374
...
main
Author | SHA1 | Date | |
---|---|---|---|
7bccb29aa6
|
|||
2b50f7c332
|
|||
0e1dc49f59 | |||
d25e54f73f | |||
58b6d1387d | |||
0e7f962645 |
@ -28,6 +28,8 @@
|
||||
"webpack-dev-server": "^5.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ckeditor/ckeditor5-build-classic": "^41.4.2",
|
||||
"@ckeditor/ckeditor5-react": "^7.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
@ -36,10 +38,14 @@
|
||||
"@uiw/react-markdown-editor": "^5.12.1",
|
||||
"axios": "^1.6.8",
|
||||
"bootstrap": "^5.3.3",
|
||||
"inputmask": "^5.0.9",
|
||||
"primereact": "^10.6.6",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.1",
|
||||
"react-cookie": "^7.1.4",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-qr-code": "^2.0.15",
|
||||
"react-router-dom": "^6.22.1"
|
||||
}
|
||||
}
|
||||
|
14
src/App.jsx
14
src/App.jsx
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useLayoutEffect } from "react";
|
||||
import React, { useState, useEffect, useLayoutEffect, useRef } from "react";
|
||||
import { Outlet, useNavigate } from "react-router-dom";
|
||||
import { FormsData, UserData, TypeAnswerData, answersData } from "./context";
|
||||
import { useCookies } from "react-cookie";
|
||||
@ -20,6 +20,9 @@ const App = () => {
|
||||
const navigate = useNavigate();
|
||||
const [forms, setForms] = useState([]);
|
||||
const [user, setUser] = useState(false);
|
||||
globalRender(window.location.pathname, user, navigate);
|
||||
|
||||
const userRef = useRef(false)
|
||||
const [answersList, setAnswersList] = useState([]);
|
||||
const [listTypeAnswer, setListTypeAnswer] = useState([
|
||||
{id: 1, text: 'Краткий ответ', typeTag: InputText},
|
||||
@ -37,13 +40,16 @@ const App = () => {
|
||||
useEffect(() => {
|
||||
async function verifyUser() {
|
||||
const response = await verifyUserApi(cookies.token);
|
||||
console.log("verifyUser App")
|
||||
|
||||
if (response.status === 200) {
|
||||
setUser(response.data);
|
||||
globalRender(window.location.pathname, response.data, navigate);
|
||||
userRef.current = response.data
|
||||
// globalRender(window.location.pathname, response.data, navigate);
|
||||
}
|
||||
else {
|
||||
globalRender(window.location.pathname, false, navigate);
|
||||
// globalRender(window.location.pathname, false, navigate);
|
||||
setUser(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +57,7 @@ const App = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<UserData.Provider value={{ user, setUser }}>
|
||||
<UserData.Provider value={{ user, setUser, userRef }}>
|
||||
<answersData.Provider value={{ answersList, setAnswersList }}>
|
||||
<FormsData.Provider value={{ forms, setForms }}>
|
||||
<TypeAnswerData.Provider
|
||||
|
@ -12,8 +12,8 @@
|
||||
}
|
||||
|
||||
.admin {
|
||||
width: 70%;
|
||||
height: 70%;
|
||||
width: 85%;
|
||||
height: 80%;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 16px;
|
||||
@ -84,7 +84,7 @@
|
||||
}
|
||||
&__item {
|
||||
width: 100%;
|
||||
height: 20%;
|
||||
height: 60px;
|
||||
border-bottom: 1px solid rgb(200, 200, 200);
|
||||
position: relative;
|
||||
&__wrapper {
|
||||
|
166
src/assets/styles/articles.module.scss
Normal file
166
src/assets/styles/articles.module.scss
Normal file
@ -0,0 +1,166 @@
|
||||
.main {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
padding: 4% 8%;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.panel {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: end;
|
||||
margin-bottom: 25px;
|
||||
gap: 15px;
|
||||
div:first-child {
|
||||
flex-grow: 1;
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.listforms {
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
height: 70%;
|
||||
min-height: 700px;
|
||||
margin-bottom: 50px;
|
||||
width: 100%;
|
||||
&__columns {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
padding: 20px 25px;
|
||||
border-bottom: 1px solid rgb(220, 220, 220);
|
||||
&__item {
|
||||
font-size: 15px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
&__forms {
|
||||
min-height: 100%;
|
||||
display: inline;
|
||||
overflow-y: auto;
|
||||
&__item {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 20px 25px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
position: relative;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
&:hover {
|
||||
background-color: rgba(240, 240, 240, 0.8);
|
||||
}
|
||||
&__title {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
&__answers {
|
||||
width: 33.3%;
|
||||
text-align: center;
|
||||
}
|
||||
&__update {
|
||||
width: 33.3%;
|
||||
text-align: center;
|
||||
}
|
||||
i {
|
||||
position: absolute;
|
||||
font-size: 15px;
|
||||
right: 30px;
|
||||
top: calc(50% - 7px);
|
||||
cursor: pointer;
|
||||
}
|
||||
ul {
|
||||
li {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.listArticles {
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
height: 70%;
|
||||
min-height: 700px;
|
||||
margin-bottom: 50px;
|
||||
width: 100%;
|
||||
&__columns {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
padding: 20px 25px;
|
||||
border-bottom: 1px solid rgb(220, 220, 220);
|
||||
&__item {
|
||||
width: 33.3%;
|
||||
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: start;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 20px 25px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
position: relative;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
&:hover {
|
||||
background-color: rgba(240, 240, 240, 0.8);
|
||||
}
|
||||
&__title {
|
||||
width: 33.3%;
|
||||
// text-align: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
&__date {
|
||||
width: 33.3%;
|
||||
// text-align: center;
|
||||
}
|
||||
&__author {
|
||||
width: 33.3%;
|
||||
// text-align: center;
|
||||
}
|
||||
i {
|
||||
position: absolute;
|
||||
font-size: 15px;
|
||||
right: 30px;
|
||||
top: calc(50% - 7px);
|
||||
cursor: pointer;
|
||||
}
|
||||
ul {
|
||||
li {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,27 @@
|
||||
padding: 0 1%;
|
||||
}
|
||||
&__file {
|
||||
// margin-top: 10px;
|
||||
display: none;
|
||||
}
|
||||
&__fileLabel {
|
||||
margin-top: 10px;
|
||||
border: 1px solid rgb(100, 100, 100);
|
||||
background-color: rgb(240, 240, 240);
|
||||
font-family: "Montserrat", sans-serif;
|
||||
color: rgb(100, 100, 100);
|
||||
cursor: pointer;
|
||||
padding: 1%;
|
||||
transition: 0.3s;
|
||||
&:hover {
|
||||
background-color: rgb(230, 230, 230);
|
||||
transition: 0.3s;
|
||||
}
|
||||
}
|
||||
&__listFile {
|
||||
margin-left: 10px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
color: rgb(100, 100, 100);
|
||||
}
|
||||
}
|
||||
&__comment {
|
||||
|
@ -0,0 +1,5 @@
|
||||
.main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 2%;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
.main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 2% 0;
|
||||
}
|
@ -30,5 +30,27 @@
|
||||
&:hover {
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
&__green {
|
||||
color: white;
|
||||
background-color: rgb(150, 209, 158);
|
||||
}
|
||||
&__white {
|
||||
color: black;
|
||||
background-color: white;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
&:hover {
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
&__transparent {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border: 1px solid rgba(0, 0, 0, 0);
|
||||
transition: 0.3s;
|
||||
&:hover {
|
||||
background-color: rgba(180, 180, 180, 0.5);
|
||||
color: white;
|
||||
transition: 0.3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -35,6 +35,14 @@
|
||||
}
|
||||
}
|
||||
&__answer {
|
||||
|
||||
&__file {
|
||||
margin-right: 10px;
|
||||
color: rgb(105, 136, 220);
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: rgb(53, 88, 182);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
121
src/assets/styles/newArticle.module.scss
Normal file
121
src/assets/styles/newArticle.module.scss
Normal file
@ -0,0 +1,121 @@
|
||||
.main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 4% 8%;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 8%;
|
||||
&__listInput {
|
||||
width: 40%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
&__date {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
span {
|
||||
position: absolute;
|
||||
font-size: 8px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
top: -40%;
|
||||
left: 2%;
|
||||
}
|
||||
}
|
||||
&__title {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
span {
|
||||
position: absolute;
|
||||
font-size: 8px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
top: -40%;
|
||||
left: 2%;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__listBtn {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
|
||||
width: 30%;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
width: 100%;
|
||||
margin: 30px 0;
|
||||
&__wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
&__input {
|
||||
position: relative;
|
||||
&__title {
|
||||
top: -40%;
|
||||
left: 2px;
|
||||
position: absolute;
|
||||
font-size: 8px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
color: rgb(100, 100, 100);
|
||||
}
|
||||
}
|
||||
&__list {
|
||||
width: 100%;
|
||||
// height: 100%;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
&__item {
|
||||
padding: 0 5px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid rgb(100, 100, 100);
|
||||
margin: 1px 3px;
|
||||
position: relative;
|
||||
span {
|
||||
font-size: 13px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
color: rgb(100, 100, 100);
|
||||
}
|
||||
&:hover span {
|
||||
visibility: hidden;
|
||||
}
|
||||
&:hover i {
|
||||
visibility: visible;
|
||||
}
|
||||
i {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: rgb(239, 73, 73);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
}
|
159
src/assets/styles/viewArticle.module.scss
Normal file
159
src/assets/styles/viewArticle.module.scss
Normal file
@ -0,0 +1,159 @@
|
||||
.main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
// height: 10%;
|
||||
&__wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-bottom: none;
|
||||
border-radius: 5px 5px 0 0;
|
||||
padding: 0 5px;
|
||||
&__article {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
&__title {
|
||||
width: 20%;
|
||||
height: 75%;
|
||||
display: flex;
|
||||
border-right: 1px solid rgb(180, 180, 180);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
&__name {
|
||||
position: absolute;
|
||||
top: -20%;
|
||||
left: 3px;
|
||||
font-size: 8px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
&__text {
|
||||
font-size: 15px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
}
|
||||
&__tags {
|
||||
width: 60%;
|
||||
height: 75%;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
padding: 5px;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
// &::-webkit-scrollbar {
|
||||
// width: 10px;
|
||||
// }
|
||||
// &::-webkit-scrollbar-thumb {
|
||||
// background-color: rgb(200, 200, 200);
|
||||
// }
|
||||
// &::-webkit-scrollbar-button:single-button {
|
||||
// background-color: #bbbbbb;
|
||||
// display: block;
|
||||
// border-style: solid;
|
||||
// height: 10px;
|
||||
// width: 16px;
|
||||
// }
|
||||
&__item {
|
||||
padding: 0 5px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid rgb(100, 100, 100);
|
||||
margin: 1px 3px;
|
||||
span {
|
||||
}
|
||||
}
|
||||
}
|
||||
&__owner {
|
||||
width: 20%;
|
||||
height: 75%;
|
||||
border-left: 1px solid rgb(180, 180, 180);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
&__name {
|
||||
position: absolute;
|
||||
top: -20%;
|
||||
right: 3px;
|
||||
font-size: 8px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
&__text {
|
||||
font-size: 15px;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__tags {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
&__item {
|
||||
padding: 0 5px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid rgb(100, 100, 100);
|
||||
margin: 1px 3px;
|
||||
span {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
&__wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 5px;
|
||||
border-radius: 0 0 5px 5px;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
height: 7px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgb(200, 200, 200);
|
||||
}
|
||||
&__article {
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .image {
|
||||
// width: 400px;
|
||||
// height: 200px;
|
||||
// border: 4px solid red
|
||||
// img {
|
||||
// width: 400px;
|
||||
// height: 200px;
|
||||
// aspect-ratio: 1000/700;
|
||||
// }
|
||||
// }
|
@ -26,7 +26,8 @@ const AnswerModal = ({
|
||||
setComment,
|
||||
setCurrentTypeAnswer,
|
||||
setFile,
|
||||
removeOptionAnswer
|
||||
removeOptionAnswer,
|
||||
addFiles
|
||||
}) => {
|
||||
|
||||
return (
|
||||
@ -45,13 +46,15 @@ const AnswerModal = ({
|
||||
<div className={classes.myModal__dialog__content__body__answer}>
|
||||
<span className={classes.myModal__dialog__content__body__answer__title}>Вопрос</span>
|
||||
<MarkDowm mkValue={question} setMkValue={setQuestion}></MarkDowm>
|
||||
<label for="fileUpload" className={classes.myModal__dialog__content__body__answer__fileLabel}>Добавить файл</label>
|
||||
<label for="fileUpload" className={classes.myModal__dialog__content__body__answer__listFile}>Файлы : {file ? file.map(item => `${item.name}; `) : Нет}</label>
|
||||
<input
|
||||
id="fileUpload"
|
||||
type="file"
|
||||
multiple
|
||||
accept="image/*,image/jpeg,video/mp4,video/x-m4v,video/*"
|
||||
className={classes.myModal__dialog__content__body__answer__file}
|
||||
value={file}
|
||||
onChange={event => setFile(event.target.value)}
|
||||
onChange={e => addFiles(e.target.files)}
|
||||
></input>
|
||||
</div>
|
||||
<div className={classes.myModal__dialog__content__body__comment}>
|
||||
|
@ -5,14 +5,13 @@ import MyButton from "./MyButton.jsx";
|
||||
|
||||
const DefaultModal = ({
|
||||
name,
|
||||
postfix,
|
||||
btn,
|
||||
BodyModal
|
||||
}) => {
|
||||
|
||||
return (
|
||||
<div class="modal fade myModal" className={classes.main} id={`${name}`} tabIndex="-1" aria-labelledby="exampleModalLabel" data-bs-backdrop="static" aria-hidden="true">
|
||||
<div class="modal-dialog myModal__dialog" className={classes.main__dialog}>
|
||||
<div class="modal-dialog modal-dialog-centered myModal__dialog" className={classes.main__dialog}>
|
||||
<div class="modal-content" className={classes.main__dialog__content}>
|
||||
{BodyModal}
|
||||
<div class="modal-footer myModal__dialog__content__footer" className={classes.myModal__dialog__content__footer}>
|
||||
|
@ -4,11 +4,23 @@ import MarkdownEditor from "@uiw/react-markdown-editor";
|
||||
import { parseTotalLinkVideo } from "../hooks/sundry/parseLinkVideo";
|
||||
|
||||
const GeneratingFormFields = ({listBlock, listTypeAnswer, answers, updateAnswersForm}) => {
|
||||
|
||||
const getUint8Array = (binary) => {
|
||||
let bytesArray = []
|
||||
let len = binary.length
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytesArray.push(binary.charCodeAt(i))
|
||||
}
|
||||
|
||||
return [new Uint8Array(bytesArray)]
|
||||
}
|
||||
|
||||
return (
|
||||
listBlock.map((item, i) =>
|
||||
<div className={classes.item} key={i}>
|
||||
<div className={classes.item__question}>
|
||||
<p className={classes.item__question__text}>{i + 1}) <MarkdownEditor.Markdown source={item.question}/></p>
|
||||
<p className={classes.item__question__text}><MarkdownEditor.Markdown source={`${i + 1}) ${item.question}`}/></p>
|
||||
<p className={classes.item__question__comment}>{item.comment}</p>
|
||||
<div className={classes.item__question__video}>
|
||||
{item.video ?
|
||||
@ -37,6 +49,13 @@ const GeneratingFormFields = ({listBlock, listTypeAnswer, answers, updateAnswers
|
||||
updateAnswersForm: updateAnswersForm ? updateAnswersForm : false
|
||||
})
|
||||
}
|
||||
{
|
||||
item.file.map(f => <a className={classes.item__answer__file} download={f.name} href={
|
||||
URL.createObjectURL(
|
||||
new Blob(getUint8Array(atob(f.binary)), { type: f.type })
|
||||
)
|
||||
}>{f.name}</a>)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ import classes from "../assets/styles/components/myButton.module.scss"
|
||||
const MyButton = (props) => {
|
||||
|
||||
return (
|
||||
<div className={classes.main}>
|
||||
<div className={classes.main} style={props.mainStyle}>
|
||||
<button
|
||||
type="button"
|
||||
className={classes[props.class]}
|
||||
|
@ -1,9 +1,20 @@
|
||||
import React from "react";
|
||||
import { InputMask } from "primereact/inputmask";
|
||||
import classes from "../assets/styles/components/myInput.module.scss";
|
||||
|
||||
const MyInput = (props) => {
|
||||
return (
|
||||
<div className={classes.main} style={{...props.otherMainStyle}}>
|
||||
{
|
||||
props.mask ?
|
||||
<InputMask
|
||||
type={props.type}
|
||||
placeholder={props.placeholder}
|
||||
style={{...props.otherInputStyle}}
|
||||
onChange={(e) => props.change(e.target.value)}
|
||||
value={props.value}
|
||||
mask={props.mask}
|
||||
/> :
|
||||
<input
|
||||
type={props.type}
|
||||
placeholder={props.placeholder}
|
||||
@ -11,6 +22,7 @@ const MyInput = (props) => {
|
||||
onChange={(e) => props.change(e.target.value)}
|
||||
value={props.value}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
11
src/components/MyQrCode.jsx
Normal file
11
src/components/MyQrCode.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import React from "react";
|
||||
import QRCode from "react-qr-code";
|
||||
|
||||
|
||||
const MyQrCode = ({value}) => {
|
||||
return (
|
||||
<QRCode value={value}/>
|
||||
)
|
||||
}
|
||||
|
||||
export default MyQrCode
|
@ -14,7 +14,8 @@ const NavBar = ({navigate, auth, setAuth}) => {
|
||||
auth ?
|
||||
auth.is_admin ?
|
||||
<><span onClick={() => navigate("/forms")}>Мои формы</span>
|
||||
<span onClick={() => navigate("/admin")}>Админ панель</span></> :
|
||||
<span onClick={() => navigate("/admin")}>Админ панель</span>
|
||||
<span onClick={() => navigate("/articles")}>Мои статьи</span></> :
|
||||
<span></span> :
|
||||
<span></span>
|
||||
}
|
||||
|
57
src/components/TextEditor.jsx
Normal file
57
src/components/TextEditor.jsx
Normal file
@ -0,0 +1,57 @@
|
||||
import React, { Component } from 'react';
|
||||
import { CKEditor } from '@ckeditor/ckeditor5-react';
|
||||
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
|
||||
const TextEditor = ({data, setData}) => {
|
||||
|
||||
function uploadAdapter(loader) {
|
||||
return {
|
||||
upload: () => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const file = await loader.file;
|
||||
const buff = await file.arrayBuffer()
|
||||
const bytesArray = new Uint8Array(buff)
|
||||
const bytesString = btoa(String.fromCharCode.apply(null, bytesArray))
|
||||
resolve({
|
||||
default: `data:image/jpg;base64, ${bytesString}`
|
||||
});
|
||||
} catch (error) {
|
||||
reject("Hello");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uploadPlugin(editor) {
|
||||
editor.plugins.get("FileRepository").createUploadAdapter = (loader) => {
|
||||
return uploadAdapter(loader);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<h2>Редактор статьи</h2>
|
||||
<CKEditor
|
||||
editor={ ClassicEditor }
|
||||
data={data}
|
||||
config = {{
|
||||
extraPlugins: [uploadPlugin],
|
||||
mediaEmbed: {previewsInData: true }
|
||||
}}
|
||||
onReady={ editor => {
|
||||
// You can store the "editor" and use when it is needed.
|
||||
console.log( 'Editor is ready to use!', editor );
|
||||
} }
|
||||
onChange={ ( event, editor ) => {
|
||||
setData(editor.getData())
|
||||
}}
|
||||
onBlur={ ( event, editor ) => {}}
|
||||
onFocus={ ( event, editor ) => {}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TextEditor
|
14
src/components/bodyModal/EditToken.jsx
Normal file
14
src/components/bodyModal/EditToken.jsx
Normal file
@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
import classes from "../../assets/styles/components/bodyModal/editToken.module.scss";
|
||||
import MyInput from "../MyInput.jsx";
|
||||
|
||||
|
||||
const EditToken = ({titleToken, setTitleToken}) => {
|
||||
return (
|
||||
<div className={classes.main}>
|
||||
<MyInput type={"text"} value={titleToken} change={setTitleToken} placeholder={"Название"}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditToken
|
@ -1,4 +1,4 @@
|
||||
import React, {useState} from "react";
|
||||
import React, {useState, useRef} from "react";
|
||||
import classes from "../../assets/styles/components/bodyModal/editUserAdminPanel.module.scss";
|
||||
import MyInput from "../../components/MyInput.jsx";
|
||||
|
||||
@ -12,6 +12,7 @@ const EditUserAdminPanel = ({
|
||||
lastName,
|
||||
login,
|
||||
phone,
|
||||
pass,
|
||||
setEmail,
|
||||
setFirstName,
|
||||
setIsAdmin,
|
||||
@ -19,10 +20,9 @@ const EditUserAdminPanel = ({
|
||||
setLastName,
|
||||
setLogin,
|
||||
setPhone,
|
||||
setPass
|
||||
}) => {
|
||||
|
||||
console.log('228', id, isAdmin, isAdmin)
|
||||
|
||||
return (
|
||||
<div className={classes.main}>
|
||||
<MyInput
|
||||
@ -35,6 +35,7 @@ const EditUserAdminPanel = ({
|
||||
/>
|
||||
<MyInput
|
||||
type={"text"}
|
||||
mask={"9 (999) 999-99-99"}
|
||||
value={phone}
|
||||
placeholder={"Телефон"}
|
||||
change={setPhone}
|
||||
@ -65,6 +66,14 @@ const EditUserAdminPanel = ({
|
||||
otherMainStyle={{width: "100%", height: "100%", marginTop: "1%"}}
|
||||
otherInputStyle={{width: "100%", height: "10%"}}
|
||||
/>
|
||||
<MyInput
|
||||
type={"text"}
|
||||
value={pass}
|
||||
placeholder={"Пароль"}
|
||||
change={setPass}
|
||||
otherMainStyle={{width: "100%", height: "100%", marginTop: "1%"}}
|
||||
otherInputStyle={{width: "100%", height: "10%"}}
|
||||
/>
|
||||
<div className={classes.main__status}>
|
||||
<div className={classes.main__status__admin}>
|
||||
<div className={classes.main__status__admin__text}>Администратор</div>
|
||||
|
13
src/components/bodyModal/QrCodeToken.jsx
Normal file
13
src/components/bodyModal/QrCodeToken.jsx
Normal file
@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import classes from "../../assets/styles/components/bodyModal/qrCodeToken.module.scss";
|
||||
import MyQrCode from "../MyQrCode.jsx";
|
||||
|
||||
const QrCodeToken = ({ value }) => {
|
||||
return (
|
||||
<div className={classes.main}>
|
||||
<MyQrCode value={`http://minerva.krbl.ru/forms/${value}`} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default QrCodeToken;
|
@ -1,42 +0,0 @@
|
||||
// import axios from "axios";
|
||||
|
||||
// async function listUsersApi(token) {
|
||||
// try {
|
||||
// const response = await axios.get(`https://api.minerva.krbl.ru/auth/manage/users`,
|
||||
// {
|
||||
// headers: {
|
||||
// "Authorization": `Token ${token}`
|
||||
// }
|
||||
// })
|
||||
// return response
|
||||
// }
|
||||
// catch (e) {
|
||||
// return e
|
||||
// }
|
||||
// };
|
||||
|
||||
// async function editUserApi(token, data) {
|
||||
// try {
|
||||
// const response = await axios.post(`https://api.minerva.krbl.ru/auth/manage/edit`,
|
||||
// {
|
||||
// "email": data.email,
|
||||
// "first_name": data.first_name,
|
||||
// "id": data.id,
|
||||
// "is_admin": data.is_admin,
|
||||
// "last_name": data.last_name,
|
||||
// "login": data.login,
|
||||
// "phone": data.phone
|
||||
// },
|
||||
// {
|
||||
// headers: {
|
||||
// "Authorization": `Token ${token}`
|
||||
// }
|
||||
// })
|
||||
// return response
|
||||
// }
|
||||
// catch (e) {
|
||||
// return e
|
||||
// }
|
||||
// };
|
||||
|
||||
// export {listUsersApi, editUserApi}
|
@ -11,7 +11,7 @@ async function getAnswersApi(token, formToken) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -29,7 +29,7 @@ async function removeAnswerFormApi(token, formId, answerId) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
144
src/hooks/api/articleApi.js
Normal file
144
src/hooks/api/articleApi.js
Normal file
@ -0,0 +1,144 @@
|
||||
import axios from "axios";
|
||||
|
||||
|
||||
async function createArticleApi(token, data) {
|
||||
try {
|
||||
const response = await axios.post(`https://api.minerva.krbl.ru/articles/new`,
|
||||
{
|
||||
"tags": [],
|
||||
"title": "Новая статья"
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Token ${token}`
|
||||
}
|
||||
})
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
async function getListArticlesApi(token) {
|
||||
try {
|
||||
const response = await axios.get(`https://api.minerva.krbl.ru/articles/list`,
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Token ${token}`
|
||||
}
|
||||
})
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
async function getArticleApi(token, articleId) {
|
||||
try {
|
||||
const response = await axios.get(`https://api.minerva.krbl.ru/articles/${articleId}/view`,
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Token ${token}`
|
||||
}
|
||||
})
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
async function editTagsApi(token, articleId, tags, flag) {
|
||||
try {
|
||||
const response = await axios.post(`https://api.minerva.krbl.ru/articles/edit/${articleId}/tags`,
|
||||
{
|
||||
"delete_flag": flag,
|
||||
"tags": tags,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Token ${token}`
|
||||
}
|
||||
})
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
async function editTitleArticleApi(token, articleId, title) {
|
||||
console.log('set')
|
||||
try {
|
||||
const response = await axios.post(`https://api.minerva.krbl.ru/articles/edit/${articleId}/setTitle`,
|
||||
{
|
||||
"title": title,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Token ${token}`
|
||||
}
|
||||
})
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
async function addArticleApi(token, articleId, content) {
|
||||
console.log('add')
|
||||
try {
|
||||
const response = await axios.post(`https://api.minerva.krbl.ru/articles/edit/${articleId}/add`,
|
||||
{
|
||||
"data": content,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Token ${token}`
|
||||
}
|
||||
})
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
async function editArticleApi(token, articleId, content) {
|
||||
try {
|
||||
const response = await axios.post(`https://api.minerva.krbl.ru/articles/edit/${articleId}/set`,
|
||||
{
|
||||
"order": 0,
|
||||
"data": content,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Token ${token}`
|
||||
}
|
||||
})
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export {
|
||||
createArticleApi,
|
||||
getArticleApi,
|
||||
getListArticlesApi,
|
||||
editTagsApi,
|
||||
editTitleArticleApi,
|
||||
addArticleApi,
|
||||
editArticleApi
|
||||
}
|
@ -7,7 +7,7 @@ async function logIn(login, password) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -41,7 +41,7 @@ async function verifyUserApi(token=false) {
|
||||
return response
|
||||
}
|
||||
catch(e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@ -11,7 +11,7 @@ async function listFormBlockApi(token, formId) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ async function addFormBlockApi(token, formId, data) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -48,7 +48,7 @@ async function updateBlockApi(token, formId, data, order) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -67,7 +67,7 @@ async function removeBlockApi(token, formId, order) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -86,7 +86,7 @@ async function updateOrderBlockApi(token, formId, data) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ async function saveFormApi(token) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -119,7 +119,7 @@ async function listFormBlockByTokenApi(token, formToken) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -135,7 +135,7 @@ async function saveAnswersApi(token, formToken, data) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -153,7 +153,7 @@ async function getBlockApi(token, formId, order) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,7 @@ async function listFormsApi(token) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -30,7 +30,7 @@ async function createFormApi(token) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -44,7 +44,7 @@ async function removeFormApi(token, formId) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -62,15 +62,15 @@ async function updateTitleFormApi(token, formId, title) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
async function newFormTokenApi(token, formId) {
|
||||
async function newFormTokenApi(token, formId, name) {
|
||||
try {
|
||||
const response = await axios.post(`https://api.minerva.krbl.ru/formBuilder/edit/${formId}/access/new`,
|
||||
{
|
||||
"name": "test"
|
||||
"name": name
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@ -80,7 +80,7 @@ async function newFormTokenApi(token, formId) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -96,7 +96,7 @@ async function listFormsByTokenApi(token, formId) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -114,9 +114,29 @@ async function removeTokenFormApi(token, formId, formToken) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
async function editTokenFormApi(token, formId, formToken, name) {
|
||||
try {
|
||||
const response = await axios.post(`https://api.minerva.krbl.ru/formBuilder/edit/${formId}/access/revoke`,
|
||||
{
|
||||
"token_id": formToken,
|
||||
"is_active": true,
|
||||
"name": name
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Token ${token}`
|
||||
}
|
||||
})
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
listFormsApi,
|
||||
@ -125,5 +145,6 @@ export {
|
||||
updateTitleFormApi,
|
||||
newFormTokenApi,
|
||||
listFormsByTokenApi,
|
||||
removeTokenFormApi
|
||||
removeTokenFormApi,
|
||||
editTokenFormApi
|
||||
};
|
@ -21,8 +21,7 @@ async function editUserApi(token, data) {
|
||||
return response
|
||||
}
|
||||
catch(e) {
|
||||
console.log(e)
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -38,7 +37,7 @@ async function getListUserApi(token) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -63,7 +62,7 @@ async function addUserApi(token, data) {
|
||||
return response
|
||||
}
|
||||
catch (e) {
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
@ -82,8 +81,27 @@ async function removeUserApi(token, login) {
|
||||
return response
|
||||
}
|
||||
catch(e) {
|
||||
console.log(e)
|
||||
return e
|
||||
return e.response
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
async function editUserPass(token, login, pass) {
|
||||
try {
|
||||
const response = await axios.post("https://api.minerva.krbl.ru/auth/manage/updatePassword",
|
||||
{
|
||||
"login": login,
|
||||
"password": pass
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Token ${token}`,
|
||||
},
|
||||
})
|
||||
return response
|
||||
}
|
||||
catch(e) {
|
||||
return e.response
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,5 +109,6 @@ export {
|
||||
editUserApi,
|
||||
getListUserApi,
|
||||
addUserApi,
|
||||
removeUserApi
|
||||
removeUserApi,
|
||||
editUserPass
|
||||
}
|
@ -13,11 +13,13 @@ function responseDataToListBlock(data) {
|
||||
}
|
||||
|
||||
for (let param of block.data) {
|
||||
|
||||
if (Array.isArray(param.Value) && param.Value.length) {
|
||||
newParam[param.Key] = param.Value.map(item => ({
|
||||
id: item.find(obj => obj.Key === "id").Value,
|
||||
text: item.find(obj => obj.Key === "text").Value
|
||||
id: item.find(obj => obj.Key === "id") ? item.find(obj => obj.Key === "id").Value : null,
|
||||
text: item.find(obj => obj.Key === "text") ? item.find(obj => obj.Key === "text").Value : null,
|
||||
name: item.find(obj => obj.Key === "name") ? item.find(obj => obj.Key === "name").Value : null,
|
||||
type: item.find(obj => obj.Key === "type") ? item.find(obj => obj.Key === "type").Value : null,
|
||||
binary: item.find(obj => obj.Key === "binary") ? item.find(obj => obj.Key === "binary").Value : null,
|
||||
}))
|
||||
}
|
||||
else {
|
||||
|
@ -3,7 +3,7 @@ import { useCookies } from "react-cookie";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import classes from "../assets/styles/adminPanel.module.scss";
|
||||
import { UserData } from "../context";
|
||||
import { getListUserApi, editUserApi, addUserApi, removeUserApi } from "../hooks/api/profileApi.js";
|
||||
import { getListUserApi, editUserApi, addUserApi, removeUserApi, editUserPass } from "../hooks/api/profileApi.js";
|
||||
import { verifyUser } from "../hooks/services/profile.js";
|
||||
import DefaultModal from "../components/DefaultModal.jsx";
|
||||
import EditUserAdminPanel from "../components/bodyModal/EditUserAdminPanel.jsx";
|
||||
@ -14,33 +14,36 @@ import CheckModal from "../components/CheckModal.jsx";
|
||||
const AdminPanel = () => {
|
||||
const navigate = useNavigate();
|
||||
const [cookies, setCookies] = useCookies(["user"]);
|
||||
const {user, setUser} = useContext(UserData);
|
||||
const {user, setUser, userRef} = useContext(UserData);
|
||||
const [listUser, setListUser] = useState([]);
|
||||
|
||||
const [email, setEmail] = useState("")
|
||||
const [firstName, setFirstName] = useState("")
|
||||
const [id, setId] = useState(false)
|
||||
const [isAdmin, setIsAdmin] = useState(false)
|
||||
const [isTeacher, setIsTeacher] = useState(false)
|
||||
const [lastName, setLastName] = useState("")
|
||||
const [login, setLogin] = useState("")
|
||||
const [phone, setPhone] = useState("")
|
||||
const [email, setEmail] = useState("");
|
||||
const [firstName, setFirstName] = useState("");
|
||||
const [id, setId] = useState(false);
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [isTeacher, setIsTeacher] = useState(false);
|
||||
const [lastName, setLastName] = useState("");
|
||||
const [login, setLogin] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [pass, setPass] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
async function getListUser() {
|
||||
const responseListUser = await getListUserApi(cookies.token);
|
||||
const responseCurrentUser = await verifyUser(cookies, user, setUser)
|
||||
|
||||
if (
|
||||
responseListUser.status === 200 &&
|
||||
responseCurrentUser.status === 200 &&
|
||||
responseCurrentUser.data.is_admin
|
||||
responseListUser.status === 200
|
||||
) {
|
||||
setListUser([...responseListUser.data].filter(item => item.id !== responseCurrentUser.data.id))
|
||||
setListUser(responseListUser.data)
|
||||
}
|
||||
else if (
|
||||
responseListUser.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log('responseListUser', responseListUser)
|
||||
console.log('responseCurrentUser', responseCurrentUser)
|
||||
console.log(responseListUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +59,8 @@ const AdminPanel = () => {
|
||||
setLastName("")
|
||||
setLogin("")
|
||||
setPhone("")
|
||||
}
|
||||
setPass("")
|
||||
};
|
||||
|
||||
async function addUser() {
|
||||
const currentState = {
|
||||
@ -73,9 +77,22 @@ const AdminPanel = () => {
|
||||
|
||||
if (response.status === 200) {
|
||||
setListUser([...listUser, currentState])
|
||||
|
||||
if (pass) {
|
||||
await editUserPass(cookies.token, login, pass);
|
||||
}
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
clearState()
|
||||
}
|
||||
};
|
||||
|
||||
async function editUser() {
|
||||
const currentState = {
|
||||
@ -87,14 +104,27 @@ const AdminPanel = () => {
|
||||
last_name: lastName,
|
||||
login: login,
|
||||
phone: phone
|
||||
}
|
||||
const response = await editUserApi(cookies.token, currentState)
|
||||
};
|
||||
const response = await editUserApi(cookies.token, currentState);
|
||||
|
||||
if (response.status === 200) {
|
||||
setListUser(listUser.map(item => item.id === id ? currentState : item))
|
||||
setListUser(listUser.map(item => item.id === id ? currentState : item));
|
||||
|
||||
if (pass) {
|
||||
await editUserPass(cookies.token, login, pass);
|
||||
};
|
||||
}
|
||||
clearState()
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
clearState();
|
||||
};
|
||||
|
||||
function openUser(user) {
|
||||
setEmail(user.email)
|
||||
@ -113,6 +143,15 @@ const AdminPanel = () => {
|
||||
if (response.status === 200) {
|
||||
setListUser(listUser.filter(item => item.login !== login))
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@ -168,7 +207,7 @@ const AdminPanel = () => {
|
||||
</div>
|
||||
<div className={classes.admin__wrapper__body__users__item__info}>
|
||||
{
|
||||
item.admin ?
|
||||
item.is_admin ?
|
||||
<i class="fa-solid fa-circle-check" style={{color: "rgb(150, 209, 158)"}}></i> :
|
||||
<i class="fa-solid fa-circle-xmark" style={{color: "rgb(235, 130, 130)"}}></i>}
|
||||
</div>
|
||||
@ -205,6 +244,8 @@ const AdminPanel = () => {
|
||||
setLastName={setLastName}
|
||||
setLogin={setLogin}
|
||||
setPhone={setPhone}
|
||||
pass={pass}
|
||||
setPass={setPass}
|
||||
/>
|
||||
}
|
||||
btn={[
|
||||
|
@ -3,16 +3,18 @@ import { useCookies } from "react-cookie";
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { answersData } from "../context";
|
||||
import classes from "../assets/styles/answersForm.module.scss";
|
||||
import { responseDataToListBlock, dateTimeParse } from "../hooks/sundry/parseListBlock";
|
||||
import { listFormBlockApi } from "../hooks/api/formApi";
|
||||
import { listUsersApi } from "../hooks/api/adminApi";
|
||||
import { responseDataToListBlock, dateTimeParse } from "../hooks/sundry/parseListBlock.js";
|
||||
import { listFormBlockApi } from "../hooks/api/formApi.js";
|
||||
import { getListUserApi } from "../hooks/api/profileApi.js";
|
||||
import { removeAnswerFormApi, getAnswersApi } from "../hooks/api/answersApi.js";
|
||||
import Loading from "../components/Loading.jsx";
|
||||
import CheckModal from "../components/CheckModal.jsx";
|
||||
import { UserData } from "../context";
|
||||
|
||||
const AnswersForm = () => {
|
||||
const { formId } = useParams();
|
||||
const {answersList, setAnswersList} = useContext(answersData);
|
||||
const {user, setUser} = useContext(UserData);
|
||||
const [data, setData] = useState(false);
|
||||
const [cookies, _, __] = useCookies(["user"]);
|
||||
|
||||
@ -21,7 +23,7 @@ const AnswersForm = () => {
|
||||
const result = []
|
||||
const responseBlocks = await listFormBlockApi(cookies.token, formId);
|
||||
const responseAnswers = await getAnswersApi(cookies.token, formId);
|
||||
const listUsers = await listUsersApi(cookies.token);
|
||||
const listUsers = await getListUserApi(cookies.token);
|
||||
|
||||
if (responseBlocks.status === 200 && responseBlocks.data) {
|
||||
const listBlocks = responseDataToListBlock(responseBlocks.data);
|
||||
@ -55,8 +57,14 @@ const AnswersForm = () => {
|
||||
|
||||
setData(result)
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(responseBlocks)
|
||||
console.log(response)
|
||||
}
|
||||
};
|
||||
|
||||
@ -69,6 +77,12 @@ const AnswersForm = () => {
|
||||
if (response.status === 200) {
|
||||
setData([...data].filter(item => item.id !== answerId))
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
|
138
src/pages/Articles.jsx
Normal file
138
src/pages/Articles.jsx
Normal file
@ -0,0 +1,138 @@
|
||||
import React, { useState, useContext, useEffect } from "react";
|
||||
import { useCookies } from "react-cookie";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import classes from "../assets/styles/articles.module.scss";
|
||||
import MyButton from "../components/MyButton.jsx";
|
||||
import MyInput from "../components/MyInput.jsx";
|
||||
import { UserData } from "../context";
|
||||
import CheckModal from "../components/CheckModal.jsx";
|
||||
import {
|
||||
createArticleApi,
|
||||
getListArticlesApi,
|
||||
} from "../hooks/api/articleApi.js";
|
||||
|
||||
const Articles = () => {
|
||||
const navigate = useNavigate();
|
||||
const { user, setUser } = useContext(UserData);
|
||||
const [stateLoading, setStateLoading] = useState(false);
|
||||
|
||||
const [listArticles, setListArticles] = useState([]);
|
||||
|
||||
const [cookies, _, __] = useCookies(["user"]);
|
||||
|
||||
useEffect(() => {
|
||||
async function getListArticles() {
|
||||
const response = await getListArticlesApi(cookies.token);
|
||||
|
||||
if (response.status === 200) {
|
||||
setListArticles(response.data);
|
||||
} else if (response.status === 401) {
|
||||
// navigate("/enter")
|
||||
setUser(false);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
|
||||
getListArticles();
|
||||
}, []);
|
||||
|
||||
async function createArticle() {
|
||||
const response = await createArticleApi(cookies.token);
|
||||
|
||||
if (response.status === 200) {
|
||||
navigate(`/articles/${response.data.id}/edit`);
|
||||
} else if (response.status === 401) {
|
||||
// navigate("/enter")
|
||||
setUser(false);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.main}>
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.panel}>
|
||||
<MyInput placeholder={"Поиск..."} />
|
||||
<MyButton
|
||||
click={createArticle}
|
||||
class={"main__blue"}
|
||||
otherStyle={{ width: "200px" }}
|
||||
text={
|
||||
stateLoading ? (
|
||||
<div class="spinner-border text-light" role="status">
|
||||
<span class="visually-hidden">Загрузка...</span>
|
||||
</div>
|
||||
) : (
|
||||
"Создать"
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.listArticles}>
|
||||
<div className={classes.listArticles__columns}>
|
||||
<div className={classes.listArticles__columns__item}>Название</div>
|
||||
<div className={classes.listArticles__columns__item}>
|
||||
Дата публикации
|
||||
</div>
|
||||
<div className={classes.listArticles__columns__item}>Автор</div>
|
||||
</div>
|
||||
<div className={classes.listArticles__forms}>
|
||||
{listArticles.map((item) => (
|
||||
<div className={classes.listArticles__forms__item}>
|
||||
<div
|
||||
className={classes.listArticles__forms__item__title}
|
||||
onClick={() => navigate(`/articles/${item.id}/edit`)}
|
||||
>
|
||||
{item.title}
|
||||
</div>
|
||||
<div className={classes.listArticles__forms__item__date}>
|
||||
24.06.24
|
||||
</div>
|
||||
<div className={classes.listArticles__forms__item__author}>
|
||||
kuwsh1n
|
||||
</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(`/articles/${item.id}/`)}
|
||||
>
|
||||
Открыть
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target={`#checkModaltest`}
|
||||
>
|
||||
Удалить
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<CheckModal
|
||||
postfix={"test"}
|
||||
message={`Вы хотетите удалить статью <Test article>?`}
|
||||
action={{
|
||||
execute: () => {},
|
||||
cancel: () => {},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Articles;
|
@ -6,6 +6,7 @@ import MyButton from "../components/MyButton.jsx";
|
||||
import MyInput from "../components/MyInput.jsx";
|
||||
import { FormsData, UserData } from "../context";
|
||||
import CheckModal from "../components/CheckModal.jsx";
|
||||
import { verifyUserApi } from "../hooks/api/enterAccountApi.js";
|
||||
import {
|
||||
listFormsApi,
|
||||
createFormApi,
|
||||
@ -17,22 +18,27 @@ import {
|
||||
const Forms = () => {
|
||||
const navigate = useNavigate();
|
||||
const { forms, setForms } = useContext(FormsData);
|
||||
const { user, setUser } = useContext(UserData);
|
||||
const { user, setUser, userRef } = useContext(UserData);
|
||||
const [stateLoading, setStateLoading] = useState(false);
|
||||
const [searchForm, setSearchForm] = useState("");
|
||||
|
||||
const [cookies, _, __] = useCookies(["user"]);
|
||||
const [cookies, setCookies, __] = useCookies(["user"]);
|
||||
|
||||
useEffect(() => {
|
||||
async function listForms() {
|
||||
const response = await listFormsApi(cookies.token);
|
||||
const responseForms = await listFormsApi(cookies.token);
|
||||
const responseUser = await verifyUserApi(cookies.token)
|
||||
|
||||
if (response.data) {
|
||||
setForms(response.data);
|
||||
} else if (response.status === 200 && response.data) {
|
||||
if (responseForms.data && responseUser) {
|
||||
setForms(responseForms.data.filter(item => item.owner_id === responseUser.data.id));
|
||||
} else if (responseForms.status === 200 && response.data) {
|
||||
setForms([]);
|
||||
} else if (responseForms.status === 401 || responseUser.status === 401) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
} else {
|
||||
console.log(response);
|
||||
console.log(responseForms);
|
||||
console.log(responseUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,11 +50,17 @@ const Forms = () => {
|
||||
const response = await createFormApi(cookies.token);
|
||||
setStateLoading(false);
|
||||
|
||||
if (response.data) {
|
||||
const token = await newFormTokenApi(cookies.token, response.data.id);
|
||||
if (response.data && response.status === 200) {
|
||||
const token = await newFormTokenApi(cookies.token, response.data.id, "Админ");
|
||||
navigate(`/forms/${response.data.id}/edit`);
|
||||
} else {
|
||||
console.log(response);
|
||||
} else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,8 +73,15 @@ const Forms = () => {
|
||||
|
||||
if (response.status === 200) {
|
||||
setForms([...forms.filter((item) => item.id !== id)]);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,8 +90,15 @@ const Forms = () => {
|
||||
|
||||
if (response.status === 200 && response.data.tokens) {
|
||||
navigate(`/forms/${response.data.tokens[0].id}/`);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,8 +109,15 @@ const Forms = () => {
|
||||
navigator.clipboard.writeText(
|
||||
`${window.location.host}/forms/${response.data.tokens[0].id}/`
|
||||
);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
|
178
src/pages/NewArticle.jsx
Normal file
178
src/pages/NewArticle.jsx
Normal file
@ -0,0 +1,178 @@
|
||||
import React, { useState, useContext, useEffect } from "react";
|
||||
import { useNavigate, useLocation, useParams } from 'react-router-dom';
|
||||
import { useCookies } from "react-cookie";
|
||||
import classes from "../assets/styles/newArticle.module.scss";
|
||||
import MyButton from "../components/MyButton.jsx";
|
||||
import Loading from "../components/Loading.jsx";
|
||||
import MyInput from "../components/MyInput.jsx";
|
||||
import TextEditor from "../components/TextEditor.jsx";
|
||||
import { UserData } from "../context/index.js";
|
||||
import { getArticleApi, editTagsApi, editTitleArticleApi, editArticleApi, addArticleApi } from "../hooks/api/articleApi.js";
|
||||
|
||||
const NewArticle = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { articleId } = useParams();
|
||||
const {user, setUser} = useContext(UserData)
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [title, setTitle] = useState("");
|
||||
const [tags, setTags] = useState([]);
|
||||
const [newTag, setNewTag] = useState("")
|
||||
const [ownerId, setOwnerId] = useState("");
|
||||
const [contentArticle, setContentArticle] = useState('');
|
||||
const [blocks, setBlocks] = useState('');
|
||||
|
||||
const [cookies, _, __] = useCookies(["user"]);
|
||||
|
||||
useEffect(() => {
|
||||
async function getArticle() {
|
||||
const response = await getArticleApi(cookies.token, articleId)
|
||||
|
||||
if (response.status === 200) {
|
||||
setTitle(response.data.article.title)
|
||||
setTags(response.data.article.tags ? response.data.article.tags : [])
|
||||
setOwnerId(response.data.article.owner_id)
|
||||
setContentArticle(response.data.blocks ? response.data.blocks[0].data : '')
|
||||
setBlocks(response.data.blocks ? response.data.blocks : false)
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
getArticle()
|
||||
}, [])
|
||||
|
||||
async function addTag() {
|
||||
if (newTag.length > 0 && !tags.find(item => item === newTag)) {
|
||||
const response = await editTagsApi(cookies.token, articleId, [newTag], false)
|
||||
|
||||
if (response.status === 200) {
|
||||
setTags([...tags, newTag])
|
||||
setNewTag("")
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function removeTag(item, index) {
|
||||
const response = await editTagsApi(cookies.token, articleId, [item], true)
|
||||
|
||||
if (response.status === 200) {
|
||||
const cTags = [...tags]
|
||||
cTags.splice(index, 1)
|
||||
setTags(cTags)
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
async function saveArticle() {
|
||||
const responseTitle = await editTitleArticleApi(cookies.token, articleId, title)
|
||||
const responseContentArticle = blocks ?
|
||||
await editArticleApi(cookies.token, articleId, contentArticle) :
|
||||
await addArticleApi(cookies.token, articleId, contentArticle)
|
||||
|
||||
if (responseTitle.status === 200 && responseContentArticle.status === 200) {
|
||||
navigate("/articles")
|
||||
}
|
||||
else if (
|
||||
responseTitle.status === 401 || responseContentArticle.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(responseTitle)
|
||||
console.log(responseContentArticle)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.main}>
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.header}>
|
||||
<div className={classes.header__listInput}>
|
||||
<div className={classes.header__listInput__date}>
|
||||
<span>Дата создания</span>
|
||||
<MyInput type={"datetime-local"}/>
|
||||
</div>
|
||||
<div className={classes.header__listInput__title}>
|
||||
<span>Название статьи</span>
|
||||
<MyInput type={"text"} value={title} change={setTitle}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.header__listBtn}>
|
||||
{/* <MyButton text={'Предпросмотр'} class={"main__white"}/> */}
|
||||
<MyButton text={'Опубликовать'} class={"main__green"} click={() => saveArticle()}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.tags}>
|
||||
<div className={classes.tags__wrapper}>
|
||||
<div className={classes.tags__wrapper__input}>
|
||||
<span className={classes.tags__wrapper__input__title}>Добавить тэг</span>
|
||||
<MyInput
|
||||
type={"text"}
|
||||
otherMainStyle={{border: "1px solid rgb(180, 180, 180)", borderRadius: "5px"}}
|
||||
otherInputStyle={{border: "0px solid rgb(180, 180, 180)", width: "85%"}}
|
||||
value={newTag}
|
||||
change={setNewTag}
|
||||
/>
|
||||
<MyButton
|
||||
type={"button"}
|
||||
text={<i class="fa-solid fa-arrow-right"></i>}
|
||||
class={"main__transparent"}
|
||||
mainStyle={
|
||||
{
|
||||
position: "absolute",
|
||||
right: "0",
|
||||
top: "0",
|
||||
height: "100%",
|
||||
}
|
||||
}
|
||||
otherStyle={{borderRadius: "0 5px 5px 0", padding: "3px 7px 0 7px"}}
|
||||
click={() => addTag()}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.tags__wrapper__list}>
|
||||
{tags ? tags.map((item, i) =>
|
||||
<div className={classes.tags__wrapper__list__item} key={i}>
|
||||
<span>#{item}</span>
|
||||
<i class="fa-solid fa-circle-xmark" onClick={() => removeTag(item, i)}></i>
|
||||
</div>
|
||||
) : <></>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.content}>
|
||||
<TextEditor data={contentArticle} setData={setContentArticle}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NewArticle;
|
@ -8,6 +8,7 @@ import PreviewModal from "../components/PreviewModal.jsx";
|
||||
import Loading from "../components/Loading.jsx";
|
||||
import { FormsData, TypeAnswerData } from "../context";
|
||||
import { listFormsApi, updateTitleFormApi } from "../hooks/api/listFormsApi.js";
|
||||
import { UserData } from "../context";
|
||||
import {
|
||||
saveFormApi,
|
||||
addFormBlockApi,
|
||||
@ -27,6 +28,7 @@ const NewForm = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { formId } = useParams();
|
||||
const {user, setUser} = useContext(UserData);
|
||||
const [dragElem, setDragElem] = useState(null);
|
||||
const [dropElem, setDropElem] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@ -78,13 +80,19 @@ const NewForm = () => {
|
||||
responseBlock.data &&
|
||||
responseForms.status === 200
|
||||
) {
|
||||
console.log(responseDataToListBlock(responseBlock.data))
|
||||
setListBlock(responseDataToListBlock(responseBlock.data));
|
||||
const title = responseForms.data.find(
|
||||
(item) => item.id === formId
|
||||
).title;
|
||||
setNewTitleForm(title);
|
||||
setOldTitleForm(title);
|
||||
} else {
|
||||
}
|
||||
else if (responseForms.status === 401 || responseBlock.status === 401) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
setListBlock([]);
|
||||
setNewTitleForm("Новыя форма");
|
||||
setOldTitleForm("Новыя форма");
|
||||
@ -132,6 +140,7 @@ const NewForm = () => {
|
||||
setOptionAnswer(obj.optionAnswer);
|
||||
setMandatory(obj.mandatory);
|
||||
setStateModal(obj.id);
|
||||
console.log(obj.file.map(item => item.name))
|
||||
}
|
||||
|
||||
async function updateBlock() {
|
||||
@ -160,8 +169,15 @@ const NewForm = () => {
|
||||
} else {
|
||||
setListBlock([]);
|
||||
}
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
|
||||
cleanStates();
|
||||
@ -172,13 +188,42 @@ const NewForm = () => {
|
||||
|
||||
if (response.status === 200) {
|
||||
setListBlock([...listBlock.filter((item) => item.order !== order)]);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
async function addFiles(newFile) {
|
||||
const fileData = []
|
||||
|
||||
for (let item of newFile) {
|
||||
let binary = ""
|
||||
const buff = await item.arrayBuffer()
|
||||
const bytesArray = new Uint8Array(buff)
|
||||
let len = bytesArray.byteLength;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode( bytesArray[ i ] );
|
||||
}
|
||||
|
||||
fileData.push({
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
binary: btoa(binary)
|
||||
})
|
||||
|
||||
setFile([...file, ...fileData])
|
||||
}
|
||||
}
|
||||
|
||||
async function addFormBlock() {
|
||||
console.log(optionAnswer);
|
||||
|
||||
const newBlock = {
|
||||
question: question,
|
||||
@ -200,8 +245,15 @@ const NewForm = () => {
|
||||
} else {
|
||||
setListBlock([]);
|
||||
}
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
cleanStates();
|
||||
}
|
||||
@ -237,11 +289,30 @@ const NewForm = () => {
|
||||
|
||||
setListBlock(numberingBlocks(tListBlock, "order"));
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
async function saveForm() {
|
||||
if (oldTitleFrom !== newTitleForm) {
|
||||
await updateTitleFormApi(cookies.token, formId, newTitleForm);
|
||||
const response = await updateTitleFormApi(cookies.token, formId, newTitleForm);
|
||||
|
||||
if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
cleanStates();
|
||||
@ -253,14 +324,14 @@ const NewForm = () => {
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.header}>
|
||||
<div className={classes.header__listInput}>
|
||||
<div className={classes.header__listInput__date}>
|
||||
{/* <div className={classes.header__listInput__date}>
|
||||
<span>Дедлайн выполнения</span>
|
||||
<MyInput
|
||||
type={"datetime-local"}
|
||||
value={datetime}
|
||||
change={setDatetime}
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
<div className={classes.header__listInput__title}>
|
||||
<span>Название формы</span>
|
||||
<MyInput
|
||||
@ -327,6 +398,7 @@ const NewForm = () => {
|
||||
updateBlock={updateBlock}
|
||||
setCurrentTypeAnswer={setCurrentTypeAnswer}
|
||||
removeOptionAnswer={removeOptionAnswer}
|
||||
addFiles={addFiles}
|
||||
/>
|
||||
|
||||
<div className={classes.content__newForm}>
|
||||
|
@ -33,6 +33,12 @@ const Profile = () => {
|
||||
setFirst_name(response.data.first_name);
|
||||
setLast_name(response.data.last_name);
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
@ -84,6 +90,15 @@ const Profile = () => {
|
||||
})
|
||||
setEdit(!edit)
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
navigate("/enter")
|
||||
setUser(false);
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,56 +1,93 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useContext } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useCookies } from "react-cookie";
|
||||
import classes from "../assets/styles/tokensForm.module.scss";
|
||||
import { listFormsByTokenApi, newFormTokenApi, removeTokenFormApi } from "../hooks/api/listFormsApi";
|
||||
import {
|
||||
listFormsByTokenApi,
|
||||
newFormTokenApi,
|
||||
removeTokenFormApi,
|
||||
editTokenFormApi,
|
||||
} from "../hooks/api/listFormsApi";
|
||||
import MyButton from "../components/MyButton.jsx";
|
||||
import MyInput from "../components/MyInput.jsx";
|
||||
import CheckModal from "../components/CheckModal.jsx";
|
||||
import { UserData } from "../context/index.js";
|
||||
import DefaultModal from "../components/DefaultModal.jsx";
|
||||
import EditToken from "../components/bodyModal/EditToken.jsx";
|
||||
import QrCodeToken from "../components/bodyModal/QrCodeToken.jsx";
|
||||
import QRCode from "qrcode";
|
||||
|
||||
const TokensForm = () => {
|
||||
const { formId } = useParams();
|
||||
const [tokens, setTokens] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [titleToken, setTitleToken] = useState('')
|
||||
const [tokens, setTokens] = useState([]);
|
||||
const [currentIdToken, setCurrentIdToken] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [titleToken, setTitleToken] = useState("");
|
||||
const { user, setUser } = useContext(UserData);
|
||||
const [qrCodeUrl, setQrcodeUrl] = useState("");
|
||||
|
||||
const [cookies, _, __] = useCookies(["user"]);
|
||||
|
||||
useEffect(() => {
|
||||
async function listFormsByToken() {
|
||||
const response = await listFormsByTokenApi(cookies.token, formId)
|
||||
console.log(response)
|
||||
const response = await listFormsByTokenApi(cookies.token, formId);
|
||||
|
||||
if (response.data.tokens) {
|
||||
setTokens(response.data.tokens.filter(item => item.is_active === true))
|
||||
setLoading(false)
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
if (response.status === 200) {
|
||||
setTokens(
|
||||
response.data.tokens.filter(
|
||||
(item, i) => item.is_active === true && i !== 0
|
||||
)
|
||||
);
|
||||
setLoading(false);
|
||||
} else if (response.status === 401) {
|
||||
// navigate("/enter")
|
||||
setUser(false);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
|
||||
listFormsByToken()
|
||||
}, [])
|
||||
listFormsByToken();
|
||||
}, []);
|
||||
|
||||
async function newFormToken() {
|
||||
const response = await newFormTokenApi(cookies.token, formId)
|
||||
const response = await newFormTokenApi(cookies.token, formId, titleToken);
|
||||
|
||||
if (response.status === 200) {
|
||||
setTokens([...tokens, response.data.token])
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
setTokens([...tokens, response.data.token]);
|
||||
} else if (response.status === 401) {
|
||||
// navigate("/enter")
|
||||
setUser(false);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
|
||||
// async function editTokenForm(tokenForm) {
|
||||
// const response = await editTokenFormApi(cookies.token, formId, tokenForm, titleToken)
|
||||
|
||||
// if (response.status === 200) {
|
||||
// setTokens([])
|
||||
// }
|
||||
// else if (
|
||||
// response.status === 401
|
||||
// ) {
|
||||
// // navigate("/enter")
|
||||
// setUser(false);
|
||||
// }
|
||||
// else {
|
||||
// console.log(response)
|
||||
// }
|
||||
// }
|
||||
|
||||
async function removeTokenForm(tokenForm) {
|
||||
const response = await removeTokenFormApi(cookies.token, formId, tokenForm)
|
||||
const response = await removeTokenFormApi(cookies.token, formId, tokenForm);
|
||||
|
||||
if (response.status === 200) {
|
||||
setTokens(tokens.filter(item => item.id !== tokenForm))
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
setTokens(tokens.filter((item) => item.id !== tokenForm));
|
||||
} else if (response.status === 401) {
|
||||
// navigate("/enter")
|
||||
setUser(false);
|
||||
} else {
|
||||
console.log(response);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,65 +98,142 @@ const TokensForm = () => {
|
||||
<div className={classes.answers__wrapper}>
|
||||
<div className={classes.answers__wrapper__header}>
|
||||
<h3>Токены формы</h3>
|
||||
<MyButton text={"Создать"} class={"main__green"} click={newFormToken}/>
|
||||
<MyButton
|
||||
text={"Создать"}
|
||||
class={"main__green"}
|
||||
click={() => {
|
||||
setTitleToken("");
|
||||
setCurrentIdToken(false);
|
||||
}}
|
||||
target={"#tokenModal"}
|
||||
toggle={"modal"}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.answers__wrapper__body}>
|
||||
<div className={classes.answers__wrapper__body__column}>
|
||||
<div className={classes.answers__wrapper__body__column__wrapper}>
|
||||
<div
|
||||
className={classes.answers__wrapper__body__column__wrapper}
|
||||
>
|
||||
<div>Название</div>
|
||||
<div>Токен</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{tokens ?
|
||||
tokens.map((item, i) =>
|
||||
{tokens ? (
|
||||
tokens.map((item, i) => (
|
||||
<div className={classes.answers__wrapper__body__item} key={i}>
|
||||
<div className={classes.answers__wrapper__body__item__wrapper}>
|
||||
<div className={classes.answers__wrapper__body__item__wrapper__title}>
|
||||
По умолчанию
|
||||
<div
|
||||
className={classes.answers__wrapper__body__item__wrapper}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
classes.answers__wrapper__body__item__wrapper__title
|
||||
}
|
||||
>
|
||||
{item.name}
|
||||
</div>
|
||||
<div className={classes.answers__wrapper__body__item__wrapper__token}>
|
||||
<div
|
||||
className={
|
||||
classes.answers__wrapper__body__item__wrapper__token
|
||||
}
|
||||
>
|
||||
{item.id}
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.answers__wrapper__body__item__btn}>
|
||||
<i
|
||||
{/* <i
|
||||
class="fa-solid fa-pen"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target={`#tokensModal${i}`}
|
||||
onClick={() => setTitleToken("По умолчанию")}>
|
||||
</i>
|
||||
<i class="fa-solid fa-trash" onClick={() => removeTokenForm(item.id)}></i>
|
||||
data-bs-target={`#tokenModal`}
|
||||
onClick={() => {
|
||||
setTitleToken(item.name)
|
||||
setCurrentIdToken(item.id)
|
||||
}}>
|
||||
</i> */}
|
||||
<i
|
||||
class="fa-solid fa-qrcode"
|
||||
onClick={() => {
|
||||
QRCode.toDataURL(
|
||||
`http://minerva.krbl.ru/forms/${item.id}`,
|
||||
{},
|
||||
(err, url) => {
|
||||
setQrcodeUrl(url);
|
||||
}
|
||||
);
|
||||
}}
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target={`#qrModal`}
|
||||
></i>
|
||||
<i
|
||||
class="fa-solid fa-trash"
|
||||
onClick={() => removeTokenForm(item.id)}
|
||||
></i>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade myModal" className={classes.myModal} id={`tokensModal${i}`} 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-content" className={classes.myModal__dialog__content}>
|
||||
<div class="modal-body" className={classes.myModal__dialog__content__body}>
|
||||
<MyInput type={"text"} value={titleToken} change={setTitleToken}/>
|
||||
<DefaultModal
|
||||
name={"qrModal"}
|
||||
btn={[
|
||||
{
|
||||
text: "Скачать",
|
||||
class: "main__green",
|
||||
dismiss: "modal",
|
||||
action: () => {
|
||||
const aElem = document.createElement("a");
|
||||
aElem.href = qrCodeUrl;
|
||||
aElem.download = `${item.name}.png`;
|
||||
aElem.click();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: "Отмена",
|
||||
class: "main__white",
|
||||
dismiss: "modal",
|
||||
action: () => {},
|
||||
},
|
||||
]}
|
||||
BodyModal={<QrCodeToken value={item.id} />}
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-footer myModal__dialog__content__footer" className={classes.myModal__dialog__content__footer}>
|
||||
<MyButton text={"Сохранить"} class={"main__green"} dismiss={"modal"}/>
|
||||
<MyButton text={"Отмена"} class={"main__white"} dismiss={"modal"}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
:
|
||||
))
|
||||
) : (
|
||||
<div className={classes.loading__wrapper}>
|
||||
<div class="spinner-border text-dark" className={classes.loading__wrapper__body} role="status">
|
||||
<div
|
||||
class="spinner-border text-dark"
|
||||
className={classes.loading__wrapper__body}
|
||||
role="status"
|
||||
>
|
||||
<span class="visually-hidden">Загрузка...</span>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
<DefaultModal
|
||||
name={"tokenModal"}
|
||||
btn={[
|
||||
{
|
||||
text: "Сохранить",
|
||||
class: "main__green",
|
||||
dismiss: "modal",
|
||||
action: () => newFormToken(),
|
||||
},
|
||||
{
|
||||
text: "Отмена",
|
||||
class: "main__white",
|
||||
dismiss: "modal",
|
||||
action: () => {},
|
||||
},
|
||||
]}
|
||||
BodyModal={
|
||||
<EditToken
|
||||
titleToken={titleToken}
|
||||
setTitleToken={setTitleToken}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TokensForm;
|
87
src/pages/ViewArticle.jsx
Normal file
87
src/pages/ViewArticle.jsx
Normal file
@ -0,0 +1,87 @@
|
||||
import React, { useState, useContext, useEffect, useLocation } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useCookies } from "react-cookie";
|
||||
import classes from "../assets/styles/viewArticle.module.scss";
|
||||
import { FormsData, TypeAnswerData, answersData, UserData } from "../context";
|
||||
import MyButton from "../components/MyButton.jsx";
|
||||
import { getArticleApi } from "../hooks/api/articleApi.js";
|
||||
import { getListUserApi } from "../hooks/api/profileApi.js";
|
||||
|
||||
|
||||
const ViewArticle = () => {
|
||||
const navigate = useNavigate();
|
||||
// const location = useLocation();
|
||||
const { articleId } = useParams();
|
||||
const {user, setUser} = useContext(UserData)
|
||||
|
||||
const [title, setTitle] = useState("");
|
||||
const [tags, setTags] = useState([]);
|
||||
const [newTag, setNewTag] = useState("")
|
||||
const [owner, setOwner] = useState("");
|
||||
|
||||
const [contentArticle, setContentArticle] = useState('');
|
||||
|
||||
const [cookies, _, __] = useCookies(["user"]);
|
||||
|
||||
useEffect(() => {
|
||||
async function getArticle() {
|
||||
const response = await getArticleApi(cookies.token, articleId)
|
||||
const responseUser = await getListUserApi(cookies.token)
|
||||
|
||||
if (response.status === 200) {
|
||||
setTitle(response.data.article.title)
|
||||
setTags(response.data.article.tags ? response.data.article.tags : [])
|
||||
setOwner(responseUser.status === 200 ? responseUser.data.find(item => item.id === response.data.article.owner_id).login : response.data.article.owner_id)
|
||||
setContentArticle(response.data.blocks ? response.data.blocks[0].data : '')
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false);
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
getArticle()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={classes.main}>
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.header}>
|
||||
<div className={classes.header__wrapper}>
|
||||
<div className={classes.header__wrapper__article}>
|
||||
<div className={classes.header__wrapper__article__title}>
|
||||
<span className={classes.header__wrapper__article__title__name}>Название</span>
|
||||
<span className={classes.header__wrapper__article__title__text}>{title}</span>
|
||||
</div>
|
||||
<div className={classes.header__wrapper__article__tags}>
|
||||
{/* <span className={classes.header__wrapper__article__tags__name}>Тэги</span> */}
|
||||
{tags.map(item => <div className={classes.header__wrapper__tags__item}>
|
||||
<span>#{item}</span>
|
||||
</div>)}
|
||||
</div>
|
||||
<div className={classes.header__wrapper__article__owner}>
|
||||
<span className={classes.header__wrapper__article__owner__name}>Автор</span>
|
||||
<span className={classes.header__wrapper__article__owner__text}>{owner}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.content}>
|
||||
<div className={classes.content__wrapper}>
|
||||
<div className="classes.content__wrapper__article" dangerouslySetInnerHTML={{__html: contentArticle}}>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default ViewArticle
|
@ -32,6 +32,12 @@ const ViewForm = () => {
|
||||
{id: item.id, answer: []}
|
||||
)));
|
||||
}
|
||||
else if (
|
||||
responseBlocks.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false);
|
||||
}
|
||||
else {
|
||||
setResponseStatus(responseBlocks.status)
|
||||
}
|
||||
@ -52,7 +58,6 @@ const ViewForm = () => {
|
||||
};
|
||||
|
||||
async function saveAnswers() {
|
||||
console.log(answers)
|
||||
const response = await saveAnswersApi(cookies.token, formId, answers);
|
||||
|
||||
if (response.status === 200) {
|
||||
@ -65,6 +70,15 @@ const ViewForm = () => {
|
||||
setAnswers([]);
|
||||
navigate("/forms");
|
||||
}
|
||||
else if (
|
||||
response.status === 401
|
||||
) {
|
||||
// navigate("/enter")
|
||||
setUser(false);
|
||||
}
|
||||
else {
|
||||
console.log(response)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -6,6 +6,8 @@ const protectedUrl = {
|
||||
new RegExp("/forms/\\w+/answers"),
|
||||
new RegExp("/tokens/\\w+"),
|
||||
new RegExp("/admin"),
|
||||
new RegExp("/articles"),
|
||||
new RegExp("/articles/\\w+/edit"),
|
||||
],
|
||||
Authorized: [
|
||||
"/enter"
|
||||
@ -16,6 +18,8 @@ const protectedUrl = {
|
||||
new RegExp("/forms/\\w+/answers"),
|
||||
new RegExp("/tokens/\\w+"),
|
||||
new RegExp("/admin"),
|
||||
new RegExp("/articles"),
|
||||
new RegExp("/articles/\\w+/edit"),
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,9 @@ import ViewForm from "../pages/ViewForm.jsx";
|
||||
import AdminPanel from '../pages/AdminPanel.jsx';
|
||||
import AnswersForm from '../pages/AnswersForm.jsx';
|
||||
import TokensForm from '../pages/TokensForm.jsx';
|
||||
import Articles from '../pages/Articles.jsx';
|
||||
import NewArticle from '../pages/NewArticle.jsx';
|
||||
import ViewArticle from '../pages/ViewArticle.jsx';
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@ -50,6 +53,18 @@ const router = createBrowserRouter([
|
||||
{
|
||||
path: "/tokens/:formId",
|
||||
element: <TokensForm/>
|
||||
},
|
||||
{
|
||||
path: "/articles",
|
||||
element: <Articles/>
|
||||
},
|
||||
{
|
||||
path: "/articles/:articleId/edit",
|
||||
element: <NewArticle/>
|
||||
},
|
||||
{
|
||||
path: "/articles/:articleId/",
|
||||
element: <ViewArticle/>
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
// const Inputmask = require('inputmask');
|
||||
const path = require("path");
|
||||
|
||||
module.exports = (env) => {
|
||||
|
Reference in New Issue
Block a user