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.test.local
.env.production.local
.idea
npm-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:
### `npm start`
### `yarn start`
Runs the app in the development mode.\
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.\
You will also see any lint errors in the console.
### `npm test`
### `yarn test`
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.
### `npm run build`
### `yarn build`
Builds the app for production to the `build` folder.\
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.
### `npm run eject`
### `yarn eject`
**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

@ -3,44 +3,23 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@babel/preset-typescript": "^7.17.12",
"@chakra-ui/icons": "^2.0.0",
"@chakra-ui/react": "^2.1.0",
"@ckeditor/ckeditor5-build-classic": "^34.1.0",
"@ckeditor/ckeditor5-react": "^5.0.2",
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.1",
"@types/node": "^16.11.36",
"@types/react": "^18.0.9",
"@types/react-color": "^3.0.6",
"@types/react-dom": "^18.0.4",
"axios": "^0.27.2",
"formik": "^2.2.9",
"framer-motion": "^6.3.3",
"moment": "^2.29.3",
"notistack": "^2.0.5",
"puppeteer": "^14.2.1",
"react": "^18.1.0",
"react-color": "^2.19.3",
"react-datepicker": "^4.8.0",
"react-dom": "^18.1.0",
"react-router-dom": "^6.3.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"react-time-picker": "^4.5.0",
"suneditor-react": "^3.4.0",
"ts-jest": "^28.0.3",
"typescript": "^4.6.4",
"web-vitals": "^2.1.4"
"typescript": "^4.4.2",
"web-vitals": "^2.1.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "jest",
"init-test": "ts-jest config:init",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
@ -60,15 +39,5 @@
"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 ReactDOM from 'react-dom/client';
import {BrowserRouter, Routes, Route } from "react-router-dom";
import { ChakraProvider } from '@chakra-ui/react';
import { SnackbarProvider } from 'notistack';
import Kit from "./kit";
import CreateScope from "./create/createScope";
import CreateQuestion from "./create/createQuestion";
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
// <React.StrictMode>
<ChakraProvider>
<SnackbarProvider maxSnack={3}>
<BrowserRouter>
<Routes>
<Route path="/">
<Route element={<CreateScope/>} path="/createScope"/>
</Route>
<Route element={<CreateQuestion/>} path="kit"/>
</Routes>
</BrowserRouter>
</SnackbarProvider>
</ChakraProvider>
// </React.StrictMode>
<React.StrictMode>
<App />
</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,6 +1,6 @@
{
"compilerOptions": {
"target": "es6",
"target": "es5",
"lib": [
"dom",
"dom.iterable",

9201
yarn.lock Normal file

File diff suppressed because it is too large Load Diff