This commit is contained in:
nflnkr 2022-12-04 00:37:21 +03:00
parent 55ccfba006
commit edfd9e0a05
34 changed files with 9308 additions and 42192 deletions

1
.env

@ -1 +0,0 @@
URL = "q"

@ -1 +0,0 @@
URL = "localhost:1488"

1
.gitignore vendored

@ -17,7 +17,6 @@
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
.idea
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*

8
.idea/.gitignore vendored

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/squidward.iml" filepath="$PROJECT_DIR$/.idea/squidward.iml" />
</modules>
</component>
</project>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -6,7 +6,7 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
In the project directory, you can run: In the project directory, you can run:
### `npm start` ### `yarn start`
Runs the app in the development mode.\ Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser. Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
@ -14,12 +14,12 @@ Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.\ The page will reload if you make edits.\
You will also see any lint errors in the console. You will also see any lint errors in the console.
### `npm test` ### `yarn test`
Launches the test runner in the interactive watch mode.\ Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build` ### `yarn build`
Builds the app for production to the `build` folder.\ Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance. It correctly bundles React in production mode and optimizes the build for the best performance.
@ -29,7 +29,7 @@ Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject` ### `yarn eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!** **Note: this is a one-way operation. Once you `eject`, you cant go back!**

@ -1,2 +0,0 @@
# squidward
front for squiz project

@ -1,5 +0,0 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};

40819
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,74 +1,43 @@
{ {
"name": "squidward", "name": "squidward",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@babel/preset-typescript": "^7.17.12", "@testing-library/jest-dom": "^5.14.1",
"@chakra-ui/icons": "^2.0.0", "@testing-library/react": "^13.0.0",
"@chakra-ui/react": "^2.1.0", "@testing-library/user-event": "^13.2.1",
"@ckeditor/ckeditor5-build-classic": "^34.1.0", "@types/jest": "^27.0.1",
"@ckeditor/ckeditor5-react": "^5.0.2", "@types/node": "^16.7.13",
"@emotion/react": "^11.9.0", "@types/react": "^18.0.0",
"@emotion/styled": "^11.8.1", "@types/react-dom": "^18.0.0",
"@testing-library/jest-dom": "^5.16.4", "react": "^18.2.0",
"@testing-library/react": "^13.2.0", "react-dom": "^18.2.0",
"@testing-library/user-event": "^13.5.0", "react-scripts": "5.0.1",
"@types/jest": "^27.5.1", "typescript": "^4.4.2",
"@types/node": "^16.11.36", "web-vitals": "^2.1.0"
"@types/react": "^18.0.9", },
"@types/react-color": "^3.0.6", "scripts": {
"@types/react-dom": "^18.0.4", "start": "react-scripts start",
"axios": "^0.27.2", "build": "react-scripts build",
"formik": "^2.2.9", "test": "react-scripts test",
"framer-motion": "^6.3.3", "eject": "react-scripts eject"
"moment": "^2.29.3", },
"notistack": "^2.0.5", "eslintConfig": {
"puppeteer": "^14.2.1", "extends": [
"react": "^18.1.0", "react-app",
"react-color": "^2.19.3", "react-app/jest"
"react-datepicker": "^4.8.0", ]
"react-dom": "^18.1.0", },
"react-router-dom": "^6.3.0", "browserslist": {
"react-scripts": "5.0.1", "production": [
"react-time-picker": "^4.5.0", ">0.2%",
"suneditor-react": "^3.4.0", "not dead",
"ts-jest": "^28.0.3", "not op_mini all"
"typescript": "^4.6.4", ],
"web-vitals": "^2.1.4" "development": [
}, "last 1 chrome version",
"scripts": { "last 1 firefox version",
"start": "react-scripts start", "last 1 safari version"
"build": "react-scripts build", ]
"test": "jest", }
"init-test": "ts-jest config:init", }
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.17.12",
"@types/react-datepicker": "^4.4.1",
"@types/react-time-picker": "^4.0.2",
"babel-jest": "^28.1.0",
"jest": "^28.1.0",
"jest-puppeteer": "^6.1.0",
"react-test-renderer": "^18.1.0"
}
}

@ -1,11 +0,0 @@
const axios = require('axios');
export const create = axios.create({
baseURL: "http://localhost:1488/quiz/create",
// baseURL: process.env.URL + "/quiz/create",
timeout: 1000,
method: "post",
validateStatus: function (status: number) {
return status === 201;
},
});

11
src/App.tsx Normal file

@ -0,0 +1,11 @@
import './App.css';
function App() {
return (
<div>
App
</div>
);
}
export default App;

@ -1,410 +0,0 @@
import React from 'react';
import { useField, Form, FormikProps, Formik } from 'formik';
import {
Select, Textarea, VStack, Checkbox, Button, HStack,
} from '@chakra-ui/react'
import 'suneditor/dist/css/suneditor.min.css'; // Import Sun Editor's CSS File
import Settings from "./settings";
import WorkSpace from "./workSpace";
import Header from "./header";
import { saveCondition, getCondition } from "./lsBacking";
import type {ElementsOfObject} from "./questionTypes"
const types = [
{desc:"текст", value:"text"},
{desc:"селект", value:"select"},
{desc:"чекбокс", value:"checkbox"},
{desc:"файл", value:"file"},
{desc:"кнопка", value:"button"},
{desc: "описание", value: "description"},
{desc:"ничего", value:"none"},
]
//Для первого ознакомления рекомендую свернуть все функции и почитать их названия и описания
//Заранее берётся поле children нужного родителя. Функция сама отделит все id от родительской части
//Вернёт число для текущего слоя вложенности (в случае чего надо будет ручками присоединить к строке родителя)
const getFreeNumber = (array:Array<ElementsOfObject>):number => {
// Для первого элемента в списке
if (array.length === 0) {
return(0)
} else {
//Для всех последующих элементов
//Создаём массив, состоящий из id всех существующих модалок
let indexes:Array<number> = []
//И берём только последние числа от строк, превращая их в числа
for (let i = 0; i < array.length; i++) {
//Строка состоит из чисел, разделённых пробелом.
//Делим строку на массив чисел
let stringArr = array[i].id.split(" ")
//Берём последний элемент массива
stringArr = stringArr.slice(-1)
indexes.push(Number(stringArr[0]))
}
//Сортируем в порядке возрастания
indexes.sort(function compare(a:number, b:number):number {
if (a < b) { return -1;}
if (a > b) { return 1;}
return 0;
}
)
let max = indexes[indexes.length - 1]
//Создаём массив - маску от 0 до самого высокого значения id
let mask:Array<number> = []
for (let i = 0; i <= max; i++) {
mask.push(i)
}
//Ищем разницу между существующими id окон и маской. Список пропущенных значений есть список доступных имён.
let difference = indexes
.filter((num:number):boolean => !mask.includes(num))
.concat(mask.filter((num:number):boolean => !indexes.includes(num)));
// difference - массив нехватающих в списке номеров.
// Если все окна у нас по порядку, без пропусков - нужно добавить новый номер
if (difference.length === 0) {
return(max + 1)
} else {
//Иначе добавить нехватающий
return(difference[0])
}
}
}
//Прохождение по полю children в поисках нужного id
const getIndexById = (id:string, array:Array<ElementsOfObject>):number | undefined => {
let index
for (let i = 0; i <= array.length; i++) {
if (array[i] !== undefined) {
if (array[i].id === id) {
index = i
break
}
}
}
if (typeof index === "number") {
return index
} else {
return undefined
}
}
//Извлечение информации из id для поиска пути к ребёнку.
const getObjectFromId = (id:string, array:Array<ElementsOfObject>): ElementsOfObject | undefined => {
//Делим строку на массив чисел
let IDs = id.split(" ")
// o o o o o <--
// \ / \ / | Проходимся по каждому слою. Значит номер итерации укажет на количество элементов,
// o o o <-- кторые нужно добавить к строке currentIteration для получения id родителя на этом уровне вложенности.
// \ / / (наверное, проще её создавать с нуля, зная сколько нужно будет элементов)
// o <--
const getNewIdString = (index:number) => {
let id = ""
for (let i = 0; i < index ;i++) {
id = id + " " + IDs[i]
}
return id.slice(1) //убираем пробел в начале
}
//Счётчик вложенности
let nestingCounter = 0
//В пересчётах в каждом слое вложенности сюда сохраняется найденный объект
let bufferObject: ElementsOfObject | undefined
//Считаем до какого уровня вложенности нам вообще искать ребёнка
let stopIndex = IDs.length
const searchId = (array:Array<ElementsOfObject>) => {
if (nestingCounter === stopIndex) {
//Мы достигли нужного уровня вложенности
return
} else {
nestingCounter = nestingCounter + 1
//Создаём строку id текущей вложенности, танцуя от знания порядкового номера этой вложенности
let calcId = getNewIdString(nestingCounter)
return (
//Здесь можно в заранее созданную переменную оповещать нашли ли мы нужный id. И обработать ошибку если нет
array.forEach((element:ElementsOfObject) => {
if (element.id === calcId) {
//Записываем найденный объект и ищем на следующем уровне
bufferObject = element
searchId(element.children)
}
})
)
}
}
searchId(array)
if (bufferObject !== undefined && bufferObject.id === id) {
return bufferObject
} else {
return undefined
}
//Есть два варианта поиска:
//Рекурсивно пройтись по хранилищу тупо ожидая наткнуться на нужного ребёнка
//Проходиться по каждому слою вложенности, создавая id текущего слоя. Находить объект и повторять
//Навскидку второй вариант для ветвистых деревьев быстрее и позволит показать на каком уровне вложенности потанцевально может оборваться поиск...
//при (не дай боже) неверной записи фокусного id
}
export default () => {
//Id - строится на основании всех id родителя + свободное число от 0 до +бесконечности.
//Разделение айдишников в строке посредством пробела
const [stockroom, setStockroom] = React.useState<Array<ElementsOfObject>>([
{id: "0", type: 7, children: [
{
id: "0 0", type: 7, children: [
// {id: "0 0 0", children: [], type: 4}
]
},
{
id: "0 1", type: 7, children: [
{
id: "0 1 0", children: [
{id: "0 1 0 0", children: [], type: 4},
{id: "0 1 0 1", children: [], type: 4}
], type: 7
}
]
},
{id: "0 2", type: 4, children: []}
]}
])
const [focus, setFocus] = React.useState<string | undefined>() //Хранит путь объекта (id)
// const [focus, setFocus] = React.useState<string | undefined>("1 0 1") //Хранит путь объекта
const setNewFocus = (value:string, multiFocus:boolean) => {
//Фокусы ставятся или удаляются от клика по уже созданным элементам.
if (multiFocus) {
//Клик ЛКМ + shift - мультивыбор
} else {
//Клик ЛКМ - единичный выбор.
setFocus(value)
}
}
//Функция пересоздаёт дерево с проверкой каждого объекта.
// Если объект в фокусе - создаётся новый объект и в него высыпаются старые поля, затем новые поля
//ФУНКЦИЯ УДАЛЯЕТ ДЕТЕЙ У НЕ КОНТЕЙНЕРОВ (контейнер div и селект)
const createNewTreeFields = (value:Object, tree = stockroom):Array<ElementsOfObject> => {
return (
tree.map((node: ElementsOfObject) => {
if (node.type !== 7 && node.type !== 1) {
node.children = []
}
if (node.id === focus) {
let obj = {
...node,
...value,
}
return (obj)
} else if (node.children.length !== 0) {
return {
...node,
children: createNewTreeFields(value, node.children)
}
} else {
return node
}
})
)
}
//Создание дерева с обновлённым объектом
//ФУНКЦИЯ УДАЛЯЕТ ДЕТЕЙ У НЕ КОНТЕЙНЕРОВ (контейнер div и селект)
const createNewTreeObject = (value:ElementsOfObject, tree = stockroom):Array<ElementsOfObject> => {
return (
tree.map((node: ElementsOfObject) => {
if (node.type !== 7 && node.type !== 1) {
node.children = []
}
if (node.id === value.id) {
return value
} else if (node.children.length !== 0) {
return {
...node,
children: createNewTreeObject(value, node.children)
}
} else {
return node
}
})
)
}
const typeHC = (type:number): void => {
if (focus !== undefined) {
setStockroom(createNewTreeFields({type:type}))
}
}
const colorHC = (color:string) => {
if (focus !== undefined) {
setStockroom(createNewTreeFields({color:color}))
}
}
const createObject = (type:number = 4) => {
if (focus !== undefined) {
//Если фокусный элемент - контейнер, добавление происходит внутрь него
//Если фокусный элемент - не контейнер, добавление происходит после объекта с фокусом
const focusedObj = getObjectFromId(focus, stockroom)
if (focusedObj !== undefined) {
if (focusedObj.children !== undefined && focusedObj.type === 7) {
//Кладём внутрь фокусного объекта
//Находим свободный ид и прикрепляем его к родительскому id
let newId = getFreeNumber(focusedObj.children).toString()
newId = focusedObj.id + " " + newId
//Создаём новый объект
const newObj = {
id: newId,
type: type,
children: []
}
focusedObj.children.push(newObj)
//Создаём дерево с обновлённым родителем
const newTree = createNewTreeObject(focusedObj)
setStockroom(newTree)
} else {
//Кладём рядом с фокусным объектом
//Находим родителя
//Отфигачиваем последний id
let parentId:string | string[] = focus.split(" ")
parentId.pop()
parentId = parentId.join(' ')
const parentObj = getObjectFromId(parentId, stockroom)
if (parentObj !== undefined) {
//Находим свободный ид и прикрепляем его к родительскому id
let newId:string = getFreeNumber(parentObj.children).toString()
newId = parentId + " " + newId
//Создаём новый объект
const newObj = {
id: newId,
type: type,
children: []
}
//Создаём новый массив для поля-children-родителя и присваиваем этот массив
let newChildrenArr = []
for (let i = 0; parentObj.children.length > i ; i++) {
let node = parentObj.children[i]
if (node.id === focus) {
newChildrenArr.push(node)
newChildrenArr.push(newObj)
} else {
newChildrenArr.push(node)
}
}
parentObj.children = newChildrenArr
//Создаём дерево с обновлённым родителем
const newTree = createNewTreeObject(parentObj)
setStockroom(newTree)
}
}
}
}
}
const deleteObject = () => {
if (focus !== undefined) {
//Если просто запретить вкладывать объект при создании дерева - будет массив со значением undefined
//Поэтому скажем родителю, что его массив опустел
const focusedObj = getObjectFromId(focus, stockroom)
//Отфигачиваем последний id
let parentId:string | string[] = focus.split(" ")
parentId.pop()
parentId = parentId.join(' ')
const parentObj = getObjectFromId(parentId, stockroom)
if (parentObj !== undefined) {
//Создаём новый массив для поля-children-родителя, не включая в него фокусный объект
let newChildrenArr = []
for (let i = 0; parentObj.children.length > i ; i++) {
let node = parentObj.children[i]
if (node.id !== focus) {
newChildrenArr.push(node)
}
}
parentObj.children = newChildrenArr
let newTree = createNewTreeObject(parentObj)
setStockroom(newTree)
}
}
}
return(
<>
<Header/>
<HStack
justifyContent="space-between"
alignItems="normal"
>
<VStack
minWidth="200px"
bgColor="lightgray"
height="98vh"
padding="10px"
>
</VStack>
<VStack>
<WorkSpace
stockroom={stockroom}
setNewFocus={setNewFocus}
focus={focus}
/>
</VStack>
<VStack
minWidth="250px"
bgColor="lightgray"
height="98vh"
padding="10px"
overflow="auto"
>
<Settings
types={types}
stockroom={stockroom}
typeHC={typeHC}
focus={focus}
changeFocus={stockroom}
changeBgColor={colorHC}
changeText={stockroom}
getIndexById={getIndexById}
createObject={createObject}
deleteObject={deleteObject}
/>
<Button onClick={() => {
console.log(stockroom)
}}>info</Button>
</VStack>
</HStack>
</>
)
}

@ -1,204 +0,0 @@
import React from 'react';
import moment from 'moment';
import {
Input, Select, Stack, Checkbox, Box, Text, Divider, Button, useNumberInput,
} from '@chakra-ui/react'
import { Formik, Field, Form } from 'formik';
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import TimePicker from 'react-time-picker';
import {create} from "../API/quiz";
interface a {
name?: string,
description?: string,
config?: string,
status?: string,
fingerprinting?: boolean,
repeatable?: boolean,
note_prevented?: boolean,
mail_notifications?: boolean,
unique_answers?: boolean,
pausable?: boolean,
time_of_passing?: number,
due_to?: number,
limit?: number,
question_cnt?: number
}
const statusValues: any = ['draft', 'template', 'stop', 'start']
export default () => {
const [isSaved, setSaved] = React.useState(false)
const [isSend, setSend] = React.useState(false)
const Options = React.useMemo(() => () => (
statusValues.map((e:string,i:number)=> {
return <option key={i} value={e}>{e}</option>
})
), []);
const { getInputProps, getIncrementButtonProps, getDecrementButtonProps } =
useNumberInput({
min: 0,
})
const input = getInputProps()
return(
<Box
padding="20px"
>
<Formik
initialValues={{
name: "",
description: "",
config: "",
status: "draft",
fingerprinting: false,
repeatable: false,
note_prevented: false,
mail_notifications: false,
unique_answers: false,
time_of_passing: "",
pausable: false,
due_to: null,
limit: 0
}}
onSubmit={(values) => {
console.log(values)
setSend(true)
let inputBody:a = {
"status": values.status,
};
if (values.name.length !== 0) {inputBody.name = values.name}
if (values.description.length !== 0) {inputBody.description = values.description}
if (values.due_to !== null) {inputBody.due_to = Number(moment(values.due_to).format("x"))}
if (values.due_to !== null) {console.log(moment(values.due_to).format("x"))}
if (values.time_of_passing.length !== 0) {inputBody.time_of_passing = moment.duration(values.time_of_passing).asSeconds()}
if (values.time_of_passing.length !== 0) {console.log(moment.duration(values.time_of_passing).asSeconds())}
if (values.limit !== 0) {inputBody.limit = values.limit}
if (values.fingerprinting) {inputBody.fingerprinting = true}
if (values.repeatable) {inputBody.repeatable = true}
if (values.note_prevented) {inputBody.note_prevented = true}
if (values.mail_notifications) {inputBody.mail_notifications = true}
if (values.unique_answers) {inputBody.unique_answers = true}
if (values.pausable) {inputBody.pausable = true}
console.log(inputBody)
create.request({body: inputBody})
.then((e: any) => {
console.log(e)
console.log(e.message)
setSaved(true)
setSend(false)
})
.catch((e: any) => {
console.log(e)
console.log(e.message)
setSaved(false)
setSend(false)
})
}}
>
{({ values, setFieldValue }) => {
return (
<Form>
<label htmlFor="firstName">First Name</label>
<Stack
backgroundColor="lightgray"
padding="20px"
>
<Text>Название опроса (максимум 280 символов)</Text>
<Field as={Input} placeholder='name' name="name" />
<Text>Описание опроса</Text>
<Field as={Input} placeholder='description' name="description"/>
<Field as={Input} type="hidden" placeholder='config' name="config"/>
<Text>Статус опроса</Text>
<Field as={Select} name="status">
{
<Options/>
}
</Field>
<Text>Количество перепрохождений</Text>
<Field
as={Input}
{...input}
name="limit"
value={values.limit}
onChange={(e:any) => {
setFieldValue("limit", Number(e.target.value))
}}
/>
</Stack>
<Stack padding="20px">
<Text>Сохранить устройство</Text>
<Field as={Checkbox} name="fingerprinting" isChecked={values.fingerprinting}/>
<Divider/>
<Text>Разрешить пользователям перепроходить опрос</Text>
<Field as={Checkbox} name="repeatable" isChecked={values.repeatable}/>
<Divider/>
<Text>Сохранять статистику неполных прохождений</Text>
<Field as={Checkbox} name="note_prevented" isChecked={values.note_prevented}/>
<Divider/>
<Text>Уведомлять по почте при каждом прохождении опроса</Text>
<Field as={Checkbox} name="mail_notifications" isChecked={values.mail_notifications}/>
<Divider/>
<Text>Сохранять статистику только для уникального прохождения опроса</Text>
<Field as={Checkbox} name="unique_answers" isChecked={values.unique_answers}/>
<Divider/>
</Stack>
<Stack
backgroundColor="lightgray"
padding="20px"
>
<Text>Время прохождения опроса</Text>
<Field
as={TimePicker}
name="time_of_passing"
format="HH : mm"
disableClock={true}
amPmAriaLabel={"am"}
onChange={(e:any) => {
if (e !== null) {
setFieldValue("time_of_passing", e)
} else {
//При стирании значения пользователем затирается и последнее значение
setFieldValue("time_of_passing", "")
}
}}
value={values.time_of_passing}
/>
<Text>Разрешить ставить на паузу</Text>
<Field as={Checkbox} name="pausable" isDisabled={values.time_of_passing.length === 0 ? true : false}/>
<Text>Дата проведения опроса</Text>
<Field
as={DatePicker}
name="due_to"
selected={values.due_to}
dateFormat="MM/dd/yyyy"
onChange={(e:any) => {
setFieldValue("due_to", e)
}}
minDate={new Date()}
/>
</Stack>
<Button
type="submit"
isLoading={isSend}
isDisabled={isSaved}
loadingText='запрос в процессе обработки'
>
{isSaved ? "Сохранено" : "Сохранить"}
</Button>
</Form>
)
}}
</Formik>
</Box>
)
}

@ -1,14 +0,0 @@
import React from 'react';
import {HStack} from "@chakra-ui/react";
export default () => {
return(
<HStack
height="2vh"
bgColor="gray"
>
</HStack>
)
}

@ -1,19 +0,0 @@
export function getCondition () {
let ls : any = window.localStorage.getItem("condition")
if (ls !== null) {
return JSON.parse(ls)
} else {
return null
}
}
export function saveCondition (key:string, value:any) {
let ls : any = getCondition()
if (ls === null) {
ls = {}
}
ls[key] = value
localStorage.setItem("condition", JSON.stringify(ls))
}
export function clearCondition (key:string, value:any) {
localStorage.setItem("condition", "{}")
}

@ -1,20 +0,0 @@
//Поля объектов, используемых для отображения созданных пользователем инструментов
interface ElementsOfObject {
text?: string;
id: string;
color?: string;
type: number
children: Array<ElementsOfObject>
}
interface QuestionProps {
type: number;
stockroom: Array<ElementsOfObject> | never[];
focus: number;
changeFocus: (id: number) => void;
changeBgColor: (text:string) => void;
changeText: (text:string) => void;
createObject?: (obj:ElementsOfObject) => void;
deleteObject: (id:number) => void;
getIndexById: (id:number, array?:ElementsOfObject) => number;
}
export type {ElementsOfObject, QuestionProps}

@ -1,123 +0,0 @@
import React from 'react';
import {Box, Button, Checkbox, Container, Select, Textarea} from "@chakra-ui/react";
import SunEditor from "suneditor-react";
import {ChromePicker} from "react-color";
import Viewer from "./viewer";
export default (props: any) => {
return (
<>
<Select
placeholder='тип вопроса'
// value={current === undefined ? 0 : current.type}
onChange={e => {
props.typeHC(Number(e.target.value))
}}
>
{
props.types.map((e: any, i: number) => (
<option key={i} value={i}>{e.desc}</option>
))
}
</Select>
<ChromePicker
disableAlpha
color={
props.current === undefined ?
""
:
props.focus === undefined ?
""
:
props.current.color
}
onChange={e => {props.changeBgColor(e.hex)}}
/>
<Button
onClick={() => {
props.createObject()
}}
>добавить</Button>
<Button
onClick={() => {
props.deleteObject()
}}
>удалить фокусный</Button>
{/*<Button*/}
{/* onClick={() => {*/}
{/* props.createObject({text: "контейнер", isFocus: true, type: 7, id: -1, parent: undefined})*/}
{/* }}*/}
{/*>контейнер</Button>*/}
{/*{*/}
{/* props.stockroom.length === 0 ?*/}
{/* null*/}
{/* :*/}
{/* <VStack style={{*/}
{/* boxShadow:"rgba(0, 0, 0, 0.3) 0px 0px 2px, rgba(0, 0, 0, 0.3) 0px 4px 8px",*/}
{/* padding:"5px",*/}
{/* width:"150px",*/}
{/* borderRadius:"4px",*/}
{/* maxHeight:"30vh",*/}
{/* minHeight:"50px",*/}
{/* overflow:"auto",*/}
{/* }}>*/}
{/* <Viewer*/}
{/* stockroom={props.stockroom}*/}
{/* changeFocus={props.changeFocus}*/}
{/* deleteObject={props.deleteObject}*/}
{/* />*/}
{/* </VStack>*/}
{/*}*/}
{/*<Button type="submit">Создать вопрос</Button>*/}
{/*{current === undefined ?*/}
{/* null*/}
{/* :*/}
{/* current.type === 5 ?*/}
{/* <SunEditor*/}
{/* width="200px"*/}
{/* onChange={(e:any)=> {*/}
{/* let visual = document.getElementById(current.id)*/}
{/* if (visual !== null) {*/}
{/* visual.innerHTML = e*/}
{/* }*/}
{/* props.changeText(e)*/}
{/* }}*/}
{/* // imageUploadHandler={(e:any)=>console.log(e)}*/}
{/* // onImageUpload={(e:any)=>console.log(e)}*/}
{/* // showController={(e:any)=>console.log(e)}*/}
{/* // hideToolbar={false}*/}
{/* defaultValue={current.text}*/}
{/* setOptions={{*/}
{/* buttonList: [*/}
{/* [*/}
{/* 'undo', 'redo',*/}
{/* 'font', 'fontSize', 'formatBlock',*/}
{/* 'paragraphStyle', 'blockquote',*/}
{/* 'bold', 'underline', 'italic', 'strike', 'subscript', 'superscript',*/}
{/* 'fontColor', 'hiliteColor', 'textStyle',*/}
{/* 'removeFormat',*/}
{/* 'outdent', 'indent',*/}
{/* 'align', 'horizontalRule', 'list', 'lineHeight',*/}
{/* 'table', 'link', 'image', 'video',*/}
{/* 'fullScreen', 'showBlocks', 'codeView',*/}
{/* 'preview', 'print', 'save', 'template',*/}
{/* ]*/}
{/* ]*/}
{/* }}*/}
{/* />*/}
{/* :*/}
{/* <Textarea*/}
{/* onChange={(e) => props.changeText(e.target.value)}*/}
{/* placeholder="Текст"*/}
{/* maxWidth="300px"*/}
{/* value={current.text}*/}
{/* />*/}
{/*}*/}
</>
)
}

@ -1,102 +0,0 @@
import React from 'react';
import {
Select, Checkbox, Button, Box, VStack, HStack, Textarea
} from '@chakra-ui/react'
// import {TextField} from "./createQuestion";
import type {QuestionProps} from "./questionTypes"
export default ({focused, element, stockroom = [], changeFocus, keyInfo, shiftKeyInfo, focus} : any) => {
switch(element.type) {
case 0://Тип элемента вопроса - текст?
return(
<Button
key={keyInfo}
backgroundColor={element.color}
sx={{border: element.id == focus? focused.border : "solid black 1px"}}
onClick={(event:any) => {
shiftKeyInfo(event, element.id)
event.target.blur()
}}
>{element.text}</Button>
)
// return (<TextField name="text"
// sx={{border: element.id == focus? focused.border : "solid black 1px"}}
// placeholder="текст"
// type="text" key={keyInfo}
// onClick={(event:any) => shiftKeyInfo(event, element.id)}/>)
break;
case 1://Тип элемента вопроса - селект?
return (
// <Select>
// {
// stockroom.map((e: any, i: number) => {
// return <option key={i}>{e.text}</option>
// })
// }
// </Select>
<Button
key={keyInfo}
backgroundColor={element.color}
sx={{border: element.id == focus? focused.border : "solid black 1px"}}
onClick={(event:any) => {
shiftKeyInfo(event, element.id)
event.target.blur()
}}
>{element.text}</Button>
)
break;
case 2://Тип элемента вопроса - чекбокс?
return(
<>
<Button
key={keyInfo}
backgroundColor={element.color}
sx={{border: element.id == focus? focused.border : "solid black 1px"}}
onClick={(event:any) => {
shiftKeyInfo(event, element.id)
event.target.blur()
}}
>{element.text}</Button>
{/*{*/}
{/* stockroom.map((e:any, i:number) => {*/}
{/* return <Checkbox key={i}>{e.text}</Checkbox>*/}
{/* })*/}
{/*}*/}
</>
)
break;
case 3://Тип элемента вопроса - файл?
return (<input type="file" key={keyInfo} onClick={(event:any) => shiftKeyInfo(event, element.id)}
style={{border: element.id == focus? focused.border : "solid black 1px"}}/>)
break;
case 4://Тип элемента вопроса - кнопка?
return(
<Button
key={keyInfo}
backgroundColor={element.color}
sx={{border: element.id == focus? focused.border : "solid black 1px"}}
onClick={(event:any) => {
shiftKeyInfo(event, element.id)
event.target.blur()
}}
>{element.text}</Button>
)
break;
case 5://Тип элемента вопроса - описание?
return(
<Box
id={element.id}
border="solid 1px #d2d2d2"
padding="10px"
key={keyInfo}
sx={{border: element.id == focus? focused.border : "solid black 1px"}}
onClick={(event:any) => shiftKeyInfo(event, element.id)}
/>
)
break;
default:
return <></>
}
}

@ -1,47 +0,0 @@
import React from 'react';
import {HStack, Button, VStack, Text} from "@chakra-ui/react";
import { DeleteIcon } from '@chakra-ui/icons'
export default (props:any) => {
return(
<>
{
props.stockroom.map((e:any, i:number) => {
return(
<HStack
key={i}
style={{
cursor:"pointer",
width:"100%",
display: "flex",
justifyContent: "center",
border: e.isFocus ? "gray 1px solid" : "none"
}}
onClick={() => {
props.changeFocus(e.id)
}}
>
{
e.type === 5 ?
<Text>Описание</Text>
:
<Text>{e.text} {e.id}</Text>
}
<Button
rightIcon={<DeleteIcon />}
onClick={(event:any) => {
event.stopPropagation()
props.deleteObject(e.id)
event.target.blur()
}}
/>
</HStack>
)
})
}
</>
)
}

@ -1,50 +0,0 @@
import React from 'react';
import Types from "./types"
const focused = {
border: "solid 2px blue"
}
export default ({stockroom, setNewFocus, focus} : any) => {
if (stockroom.length !== 0) {
const shiftKeyInfo = (event:any, id:any) => {
event.stopPropagation()
if (event.shiftKey) {
setNewFocus(id, true)
} else {
setNewFocus(id, false)
}
}
function isContains (e:any) {
return(
e.map((element:any, i:number) => {
// if (element.children.length === 0) {
if (element.type === 7) {
return(
<div key={element.id} style={{border: element.id == focus? focused.border : "solid black 1px", padding: "10px"}}
onClick={(event:any) => {
shiftKeyInfo(event, element.id)
}}
>
{isContains(element.children)}
</div>
)
} else {
return(
<Types
element={element} keyInfo={element.id} setNewFocus={setNewFocus} key={element.id} shiftKeyInfo={shiftKeyInfo} focused={focused} focus={focus}
/>
)
}
})
)
}
return isContains(stockroom)
} else {
return <></>
}
}

13
src/index.css Normal file

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

@ -1,29 +1,13 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import {BrowserRouter, Routes, Route } from "react-router-dom"; import './index.css';
import { ChakraProvider } from '@chakra-ui/react'; import App from './App';
import { SnackbarProvider } from 'notistack';
import Kit from "./kit";
import CreateScope from "./create/createScope";
import CreateQuestion from "./create/createQuestion";
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement document.getElementById('root') as HTMLElement
); );
root.render( root.render(
<React.StrictMode>
// <React.StrictMode> <App />
<ChakraProvider> </React.StrictMode>
<SnackbarProvider maxSnack={3}> );
<BrowserRouter>
<Routes>
<Route path="/">
<Route element={<CreateScope/>} path="/createScope"/>
</Route>
<Route element={<CreateQuestion/>} path="kit"/>
</Routes>
</BrowserRouter>
</SnackbarProvider>
</ChakraProvider>
// </React.StrictMode>
);

@ -1,8 +0,0 @@
import React from 'react';
import PaginatedList from "./paginatedList";
export default() => {
return(
<PaginatedList/>
)
}

@ -1,81 +0,0 @@
import React from 'react';
import { VStack, Box, Button,
Select, HStack,
} from '@chakra-ui/react';
import Pagination from "./pagination";
let source: any = []
function generation () {
if (source.length === 0) {
for ( let i = 0; i < 103; i++) {
source.push("name " + i)
}
}
}
let arrPageSize: any = [
10,
25,
50,
100
]
generation()
export default () => {
const [page, setPage] = React.useState<number>(1)
const [sizePage, setSizePage] = React.useState<number>(10)
const [countPages, setCountPages] = React.useState<number>(Math.ceil(source.length/sizePage))
const [firstIndexList, setFirstIndexList] = React.useState<number>(0)
const [arr, setArr] = React.useState<any>(source.slice(0, sizePage))
function onPageChange (page: number) {
let start = (page - 1) * sizePage
let end = start + sizePage
setArr(source.slice(start, end))
setFirstIndexList(start)
setPage(page)
}
function onSizeChange (size: number) {
let newArr = source.slice(firstIndexList, firstIndexList + size)
setArr(newArr)
setSizePage(size)
setCountPages(Math.ceil(source.length/size))
setPage(Math.floor(firstIndexList / size + 1))
}
return(
<>
<VStack width="min-content">
<Box
width={"600px"}
height={"400px"}
overflow="auto"
>
{
arr.map((e: string, i: number) => {
return <Button width="100%" key={i}>{e}</Button>
})
}
</Box>
<HStack flexWrap="wrap" width="100%" justifyContent="center">
<Pagination
count={countPages}
current={page}
onPageChange={onPageChange}
/>
<Select maxW="90px" onChange={(e) => onSizeChange(Number(e.target.value))}>
{
arrPageSize.map((e:number,i:number)=> {
return <option key={i} value={e}>{e}</option>
})
}
</Select>
</HStack>
</VStack>
</>
)
}

@ -1,83 +0,0 @@
import React from 'react';
import { ArrowLeftIcon, ArrowRightIcon } from '@chakra-ui/icons';
import { HStack, Button, Text, NumberInput, NumberInputField, NumberIncrementStepper,
NumberDecrementStepper, NumberInputStepper,
} from '@chakra-ui/react';
interface propsTS {
count: number
current: number
onPageChange(e: number): void
}
export default ({
count,
current,
onPageChange,
}: propsTS) => {
//Проверка, что пришедшие данные валидны
if(count <= 0 || current <= 0) {return <Text>Указанные значения ниже нуля</Text>}
if(count < current) {return <Text>Текущая страница больше общего числа</Text>}
// =====\/======
// |n-1||n||n+1| - указатель в середине
// =\/==========
// |1||2||3| - указатель в начале
// ===========\/
// |n-2||n-1||n| - указатель в конце
// =============
const left = current === 1 ?
// Указатель в начале
1 :
current === count ?
// указатель в конце | указатель в середине
current - 2 : current - 1
const center = current === 1 ?
// Указатель в начале
2 :
current === count ?
// указатель в конце | указатель в середине
current - 1 : current
const right = current === 1 ?
// Указатель в начале
3 :
current === count ?
// указатель в конце | указатель в середине
current : current + 1
return(
<HStack flexWrap="wrap">
<HStack>
<Button onClick={() => {onPageChange(1)}} key={0} leftIcon={<ArrowLeftIcon/>}/>
<Button isDisabled={left === 0 ? true : false} color={current === left ? "blue" : ""} onClick={() => {onPageChange(left)}} key={1}>{left === 0 ? "--" : left}</Button>
<Button color={current === center ? "blue" : ""} onClick={() => {onPageChange(center)}} key={2}>{center}</Button>
<Button isDisabled={right > count ? true : false} color={current === right ? "blue" : ""} onClick={() => {onPageChange(right)}} key={3}>{right > count ? "--" : right}</Button>
<Button onClick={() => {onPageChange(count)}} key={4} rightIcon={<ArrowRightIcon/>}/>
</HStack>
<NumberInput
size='sm'
max={count}
maxW={"80px"}
value={current}
min={1}
onChange={(e) => onPageChange(Number(e))}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<Text> из {count}</Text>
</HStack>
)
}

5
src/setupTests.ts Normal file

@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

@ -1,26 +0,0 @@
import {create} from '../API/quiz'
const inputBody = {
"fingerprinting": true,
"repeatable": true,
"note_prevented": true,
"mail_notifications": true,
"unique_answers": true,
"name": "string",
"description": "string",
"config": "",
"status": "draft",
"limit": 0,
"pausable": true,
"question_cnt": 0
};
it('Запросы положительный', () => {
expect.assertions(1);
return create.request({body: inputBody})
.then((e: any) => {
expect(e.status).toBe(201);
})
.catch((e: any) => {
expect(e.status).not.toBe(4);
})
console.log("aaa")
});

@ -1,4 +0,0 @@
import React from 'react';
export default () => {
return <button onClick={() => {alert("внимание")}}>кнопка</button>
}

@ -1,26 +1,26 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es6", "target": "es5",
"lib": [ "lib": [
"dom", "dom",
"dom.iterable", "dom.iterable",
"esnext" "esnext"
], ],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,
"jsx": "react-jsx" "jsx": "react-jsx"
}, },
"include": [ "include": [
"src" "src"
] ]
} }

9201
yarn.lock Normal file

File diff suppressed because it is too large Load Diff