diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a1bea6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +.idea diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..842b505 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "typescript.enablePromptUseWorkspaceTsdk": true, + "typescript.tsdk": "node_modules/typescript/lib" + } \ No newline at end of file diff --git a/.yarnrc b/.yarnrc new file mode 100644 index 0000000..e1a507d --- /dev/null +++ b/.yarnrc @@ -0,0 +1 @@ +"@frontend:registry" "https://penahub.gitlab.yandexcloud.net/api/v4/packages/npm/" diff --git a/craco.config.js b/craco.config.js new file mode 100644 index 0000000..fa1bc01 --- /dev/null +++ b/craco.config.js @@ -0,0 +1,17 @@ +const CracoAlias = require("craco-alias"); + +module.exports = { + plugins: [ + { + plugin: CracoAlias, + options: { + source: "tsconfig", + // baseUrl SHOULD be specified + // plugin does not take it from tsconfig + baseUrl: "./src", + // tsConfigPath should point to the file where "baseUrl" and "paths" are specified + tsConfigPath: "./tsconfig.extend.json" + } + } + ] +}; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..01e7eea --- /dev/null +++ b/package.json @@ -0,0 +1,53 @@ +{ + "name": "squzanswerer", + "version": "1.0.0", + "private": true, + "repository": "git@penahub.gitlab.yandexcloud.net:frontend/squzanswerer.git", + "author": "ryletd", + "license": "MIT", + "dependencies": { + "@craco/craco": "^7.1.0", + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@frontend/kitui": "^1.0.54", + "@mui/icons-material": "^5.15.0", + "@mui/material": "^5.15.0", + "@mui/x-date-pickers": "^6.18.4", + "axios": "^1.6.2", + "dayjs": "^1.11.10", + "notistack": "^3.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-rnd": "^10.4.1", + "react-router-dom": "^6.21.0", + "react-scripts": "^5.0.1", + "swr": "^2.2.4", + "typescript": "^5.3.3", + "use-debounce": "^10.0.0", + "zustand": "^4.4.7" + }, + "devDependencies": { + "@emoji-mart/data": "^1.1.2", + "@emoji-mart/react": "^1.1.1", + "@types/node": "^20.10.4", + "@types/react": "^18.2.45", + "@types/react-dom": "^18.2.18", + "craco-alias": "^3.0.1" + }, + "scripts": { + "start": "craco start", + "build": "craco build" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..1f72bd6 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..a96c59f --- /dev/null +++ b/public/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + Pena Quiz + + + +
+ + diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..9969837 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "android-chrome-192x192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "android-chrome-512x512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..b20abb7 --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..adb64bf --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,43 @@ +import dayjs from "dayjs"; +import "dayjs/locale/ru"; +import { ViewPage } from "./pages/ViewPublicationPage"; +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import "./index.css"; + +import { + clearAuthToken, + getMessageFromFetchError, + useUserFetcher, +} from "@frontend/kitui"; +import { clearUserData, setUser, useUserStore } from "@root/user"; +import { enqueueSnackbar } from "notistack"; + +dayjs.locale("ru"); + +export default function App() { + const userId = useUserStore((state) => state.userId); + + useUserFetcher({ + url: `https://hub.pena.digital/user/${userId}`, + userId, + onNewUser: setUser, + onError: (error) => { + const errorMessage = getMessageFromFetchError(error); + if (errorMessage) { + enqueueSnackbar(errorMessage); + clearUserData(); + clearAuthToken(); + } + }, + }); + + return ( + <> + + + } /> + + + + ); +} diff --git a/src/api/contactForm.ts b/src/api/contactForm.ts new file mode 100644 index 0000000..463bdaa --- /dev/null +++ b/src/api/contactForm.ts @@ -0,0 +1,17 @@ +import axios from "axios"; + +const domen = window.location.hostname === "localhost" ? "squiz.pena.digital" : window.location.hostname + +export function sendContactFormRequest(body: { + + contact: string; + whoami: string; +}) { + return axios(`https://${domen}/feedback/callme`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + data: body, + }); +} \ No newline at end of file diff --git a/src/api/question.ts b/src/api/question.ts new file mode 100644 index 0000000..e70b4cc --- /dev/null +++ b/src/api/question.ts @@ -0,0 +1,71 @@ +import { makeRequest } from "@frontend/kitui"; +import { CreateQuestionRequest } from "model/question/create"; +import { RawQuestion } from "model/question/question"; +import { GetQuestionListRequest, GetQuestionListResponse } from "@model/question/getList"; +import { EditQuestionRequest, EditQuestionResponse } from "@model/question/edit"; +import { DeleteQuestionRequest, DeleteQuestionResponse } from "@model/question/delete"; +import { CopyQuestionRequest, CopyQuestionResponse } from "@model/question/copy"; + + +const baseUrl = process.env.NODE_ENV === "production" ? "/squiz" : "https://squiz.pena.digital/squiz"; + +function createQuestion(body: CreateQuestionRequest) { + return makeRequest({ + url: `${baseUrl}/question/create`, + body, + method: "POST", + }); +} + +async function getQuestionList(body?: Partial) { + console.log("body" , body) + if (!body?.quiz_id) return null; + + const response = await makeRequest({ + url: `${baseUrl}/question/getList`, + body: { ...defaultGetQuestionListBody, ...body }, + method: "POST", + }); + + return response.items; +} + +function editQuestion(body: EditQuestionRequest, signal?: AbortSignal) { + return makeRequest({ + url: `${baseUrl}/question/edit`, + body, + method: "PATCH", + signal, + }); +} + +function copyQuestion(questionId: number, quizId: number) { + return makeRequest({ + url: `${baseUrl}/question/copy`, + body: { id: questionId, quiz_id: quizId }, + method: "POST", + }); +} + +function deleteQuestion(id: number) { + return makeRequest({ + url: `${baseUrl}/question/delete`, + body: { id }, + method: "DELETE", + }); +} + +export const questionApi = { + create: createQuestion, + getList: getQuestionList, + edit: editQuestion, + copy: copyQuestion, + delete: deleteQuestion, +}; + + +const defaultGetQuestionListBody: GetQuestionListRequest = { + "limit": 100, + "offset": 0, + "type": "", +}; diff --git a/src/api/quiz.ts b/src/api/quiz.ts new file mode 100644 index 0000000..140aab1 --- /dev/null +++ b/src/api/quiz.ts @@ -0,0 +1,118 @@ +import { makeRequest } from "@frontend/kitui"; +import { defaultQuizConfig } from "@model/quizSettings"; +import { CopyQuizRequest, CopyQuizResponse } from "model/quiz/copy"; +import { CreateQuizRequest } from "model/quiz/create"; +import { DeleteQuizRequest, DeleteQuizResponse } from "model/quiz/delete"; +import { EditQuizRequest, EditQuizResponse } from "model/quiz/edit"; +import { GetQuizRequest, GetQuizResponse } from "model/quiz/get"; +import { GetQuizListRequest, GetQuizListResponse } from "model/quiz/getList"; +import { RawQuiz } from "model/quiz/quiz"; + + +const baseUrl = process.env.NODE_ENV === "production" ? "/squiz" : "https://squiz.pena.digital/squiz"; +const imagesUrl = process.env.NODE_ENV === "production" ? "/squizstorer" : "https://squiz.pena.digital/squizstorer"; + +function createQuiz(body?: Partial) { + return makeRequest({ + url: `${baseUrl}/quiz/create`, + body: { ...defaultCreateQuizBody, ...body }, + method: "POST", + }); +} + +async function getQuizList(body?: Partial) { + const response = await makeRequest({ + url: `${baseUrl}/quiz/getList`, + body: { ...defaultGetQuizListBody, ...body }, + method: "POST", + }); + + return response.items; +} + +function getQuiz(body?: Partial) { + return makeRequest({ + url: `${baseUrl}/quiz/get`, + body: { ...defaultGetQuizBody, ...body }, + method: "GET", + }); +} + +async function editQuiz(body: EditQuizRequest, signal?: AbortSignal) { + return makeRequest({ + url: `${baseUrl}/quiz/edit`, + body, + method: "PATCH", + signal, + }); +} + +function copyQuiz(id: number) { + return makeRequest({ + url: `${baseUrl}/quiz/copy`, + body: { id }, + method: "POST", + }); +} + +function deleteQuiz(id: number) { + return makeRequest({ + url: `${baseUrl}/quiz/delete`, + body: { id }, + method: "DELETE", + }); +} + +function addQuizImages(quizId: number, image: Blob) { + const formData = new FormData(); + + formData.append("quiz", quizId.toString()); + formData.append("image", image); + + return makeRequest({ + url: `${imagesUrl}/quiz/putImages`, + body: formData, + method: "PUT", + }); +} + +export const quizApi = { + create: createQuiz, + getList: getQuizList, + get: getQuiz, + edit: editQuiz, + copy: copyQuiz, + delete: deleteQuiz, + addImages: addQuizImages, +}; + + +const defaultCreateQuizBody: CreateQuizRequest = { + "fingerprinting": true, + "repeatable": true, + "note_prevented": true, + "mail_notifications": false, + "unique_answers": true, + "name": "", + "description": "", + "config": JSON.stringify(defaultQuizConfig), + "status": "stop", + "limit": 0, + "due_to": 0, + "time_of_passing": 0, + "pausable": false, + "super": false, + "group_id": 0, +}; + +const defaultGetQuizBody: GetQuizRequest = { + "quiz_id": "string", + "limit": 0, + "page": 0, + "need_config": true, +}; + +const defaultGetQuizListBody: GetQuizListRequest = { + "limit": 100, + "offset": 0, +}; diff --git a/src/assets/AutoOpen.png b/src/assets/AutoOpen.png new file mode 100644 index 0000000..3ec8840 Binary files /dev/null and b/src/assets/AutoOpen.png differ diff --git a/src/assets/BannerImg.png b/src/assets/BannerImg.png new file mode 100644 index 0000000..0e38087 Binary files /dev/null and b/src/assets/BannerImg.png differ diff --git a/src/assets/Bunner.png b/src/assets/Bunner.png new file mode 100644 index 0000000..bd06b31 Binary files /dev/null and b/src/assets/Bunner.png differ diff --git a/src/assets/InBodySite.png b/src/assets/InBodySite.png new file mode 100644 index 0000000..83e66d5 Binary files /dev/null and b/src/assets/InBodySite.png differ diff --git a/src/assets/LDownButton.svg b/src/assets/LDownButton.svg new file mode 100644 index 0000000..a6fc36a --- /dev/null +++ b/src/assets/LDownButton.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/LandingPict/Desktop.png b/src/assets/LandingPict/Desktop.png new file mode 100644 index 0000000..167d67b Binary files /dev/null and b/src/assets/LandingPict/Desktop.png differ diff --git a/src/assets/LandingPict/calendarIcon.tsx b/src/assets/LandingPict/calendarIcon.tsx new file mode 100644 index 0000000..8e94dc9 --- /dev/null +++ b/src/assets/LandingPict/calendarIcon.tsx @@ -0,0 +1,81 @@ +import { Box } from "@mui/material"; + +interface Props { + color?: string; +} + +export default function CalendarIcon({ color }: Props) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/src/assets/LandingPict/notebook.tsx b/src/assets/LandingPict/notebook.tsx new file mode 100644 index 0000000..bc6caea --- /dev/null +++ b/src/assets/LandingPict/notebook.tsx @@ -0,0 +1,535 @@ +import { Box } from "@mui/material"; + +interface Props { + color?: string; +} + +export default function Notebook({ color }: Props) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/assets/LandingPict/titleIcon.tsx b/src/assets/LandingPict/titleIcon.tsx new file mode 100644 index 0000000..29039e9 --- /dev/null +++ b/src/assets/LandingPict/titleIcon.tsx @@ -0,0 +1,25 @@ +import { Box } from "@mui/material"; + + +interface Props { + color?: string; +} + +export default function TitleIcon({color}:Props) { + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/LandingPict/youtobeIcon.tsx b/src/assets/LandingPict/youtobeIcon.tsx new file mode 100644 index 0000000..cdc36ad --- /dev/null +++ b/src/assets/LandingPict/youtobeIcon.tsx @@ -0,0 +1,109 @@ +import { Box } from "@mui/material"; + +interface Props { + color?: string; + width?: string; +} + +export default function YoutobeIcon({ color, width }: Props) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/assets/OnButton.png b/src/assets/OnButton.png new file mode 100644 index 0000000..e44a91a Binary files /dev/null and b/src/assets/OnButton.png differ diff --git a/src/assets/Qr.png b/src/assets/Qr.png new file mode 100644 index 0000000..1ed96a3 Binary files /dev/null and b/src/assets/Qr.png differ diff --git a/src/assets/Quiz-main.png b/src/assets/Quiz-main.png new file mode 100644 index 0000000..815a696 Binary files /dev/null and b/src/assets/Quiz-main.png differ diff --git a/src/assets/RDownButton.svg b/src/assets/RDownButton.svg new file mode 100644 index 0000000..01933c1 --- /dev/null +++ b/src/assets/RDownButton.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/Rectangle 110.png b/src/assets/Rectangle 110.png new file mode 100644 index 0000000..1bf27f8 Binary files /dev/null and b/src/assets/Rectangle 110.png differ diff --git a/src/assets/VidjetImg.png b/src/assets/VidjetImg.png new file mode 100644 index 0000000..b659fd5 Binary files /dev/null and b/src/assets/VidjetImg.png differ diff --git a/src/assets/Widget.png b/src/assets/Widget.png new file mode 100644 index 0000000..c3f3e26 Binary files /dev/null and b/src/assets/Widget.png differ diff --git a/src/assets/card-1.png b/src/assets/card-1.png new file mode 100644 index 0000000..462ddd8 Binary files /dev/null and b/src/assets/card-1.png differ diff --git a/src/assets/card-2.png b/src/assets/card-2.png new file mode 100644 index 0000000..f0652f6 Binary files /dev/null and b/src/assets/card-2.png differ diff --git a/src/assets/card-3.png b/src/assets/card-3.png new file mode 100644 index 0000000..cbba218 Binary files /dev/null and b/src/assets/card-3.png differ diff --git a/src/assets/dots.png b/src/assets/dots.png new file mode 100644 index 0000000..6d460d7 Binary files /dev/null and b/src/assets/dots.png differ diff --git a/src/assets/icons/AlignCenterIcon.tsx b/src/assets/icons/AlignCenterIcon.tsx new file mode 100644 index 0000000..bddd42c --- /dev/null +++ b/src/assets/icons/AlignCenterIcon.tsx @@ -0,0 +1,29 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function AlignCenterIcon({ color }: Props) { + + return ( + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/AlignLeftIcon.tsx b/src/assets/icons/AlignLeftIcon.tsx new file mode 100644 index 0000000..ee2a80c --- /dev/null +++ b/src/assets/icons/AlignLeftIcon.tsx @@ -0,0 +1,27 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function AlignLeftIcon({ color }: Props) { + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/AlignRightIcon.tsx b/src/assets/icons/AlignRightIcon.tsx new file mode 100644 index 0000000..5a035db --- /dev/null +++ b/src/assets/icons/AlignRightIcon.tsx @@ -0,0 +1,27 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function AlignRightIcon({ color }: Props) { + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/ArrowCounterClockWise.svg b/src/assets/icons/ArrowCounterClockWise.svg new file mode 100644 index 0000000..ba2b9e6 --- /dev/null +++ b/src/assets/icons/ArrowCounterClockWise.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/ArrowDownIcon.tsx b/src/assets/icons/ArrowDownIcon.tsx new file mode 100644 index 0000000..0b92d66 --- /dev/null +++ b/src/assets/icons/ArrowDownIcon.tsx @@ -0,0 +1,24 @@ +import { Box, useTheme } from "@mui/material"; + + +export default function ArrowDownIcon(props: any) { + const theme = useTheme(); + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/ArrowGear.svg b/src/assets/icons/ArrowGear.svg new file mode 100644 index 0000000..735328a --- /dev/null +++ b/src/assets/icons/ArrowGear.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/assets/icons/ArrowLeftSP.tsx b/src/assets/icons/ArrowLeftSP.tsx new file mode 100644 index 0000000..1ba692e --- /dev/null +++ b/src/assets/icons/ArrowLeftSP.tsx @@ -0,0 +1,27 @@ +import {Box, SxProps, Theme, useTheme} from "@mui/material"; + + +interface Props { + right: boolean +} + +export default function ArrowLeftSP({right} : Props) { + const theme = useTheme(); + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/BackArrowIcon.tsx b/src/assets/icons/BackArrowIcon.tsx new file mode 100644 index 0000000..cbfef39 --- /dev/null +++ b/src/assets/icons/BackArrowIcon.tsx @@ -0,0 +1,26 @@ +import { Box } from "@mui/material"; + +export default function BackArrowIcon({ color = "black" }: { color?: string }) { + return ( + + + + + + + ); +} diff --git a/src/assets/icons/BrowserIcon.tsx b/src/assets/icons/BrowserIcon.tsx new file mode 100644 index 0000000..418eec9 --- /dev/null +++ b/src/assets/icons/BrowserIcon.tsx @@ -0,0 +1,40 @@ +import { Box, useTheme } from "@mui/material"; + +export default function BrowserIcon() { + const theme = useTheme(); + + return ( + + + + + + + ); +} diff --git a/src/assets/icons/Burger.tsx b/src/assets/icons/Burger.tsx new file mode 100644 index 0000000..eb24798 --- /dev/null +++ b/src/assets/icons/Burger.tsx @@ -0,0 +1,9 @@ +import { FC, SVGProps } from "react"; + +export const Burger: FC> = (props) => ( + + + + + +); diff --git a/src/assets/icons/CalendarIcon.tsx b/src/assets/icons/CalendarIcon.tsx new file mode 100644 index 0000000..a3c9129 --- /dev/null +++ b/src/assets/icons/CalendarIcon.tsx @@ -0,0 +1,39 @@ +import { Box } from "@mui/material"; + +export default function CalendarIcon() { + return ( + + + + + + + + + + + + + + + ); +} diff --git a/src/assets/icons/ChartIcon.tsx b/src/assets/icons/ChartIcon.tsx new file mode 100644 index 0000000..f8c2947 --- /dev/null +++ b/src/assets/icons/ChartIcon.tsx @@ -0,0 +1,22 @@ +import { Box, useTheme } from "@mui/material"; + + +export default function ChartIcon() { + const theme = useTheme(); + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/ChartPieIcon.tsx b/src/assets/icons/ChartPieIcon.tsx new file mode 100644 index 0000000..6671f74 --- /dev/null +++ b/src/assets/icons/ChartPieIcon.tsx @@ -0,0 +1,40 @@ +import { Box } from "@mui/material"; + +interface Props { + height: string; + width: string; + color: string; +} + +export default function ChartPieIcon({ height, width, color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/Checkbox.tsx b/src/assets/icons/Checkbox.tsx new file mode 100644 index 0000000..265b62e --- /dev/null +++ b/src/assets/icons/Checkbox.tsx @@ -0,0 +1,43 @@ +import { Box, useTheme } from "@mui/material"; + +type CheckboxIconProps = { + checked?: boolean; +}; + +export const CheckboxIcon = ({ checked = false }: CheckboxIconProps) => { + const theme = useTheme(); + + return ( + + {checked && ( + + + + )} + + ); +}; diff --git a/src/assets/icons/CloseBold.tsx b/src/assets/icons/CloseBold.tsx new file mode 100644 index 0000000..84942f7 --- /dev/null +++ b/src/assets/icons/CloseBold.tsx @@ -0,0 +1,86 @@ +import { useTheme } from "@mui/material"; + +interface Props { + width?: number; +} + +export default function CloseBold({ width }: Props) { + const theme = useTheme(); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/assets/icons/CollapseMenuIcon.tsx b/src/assets/icons/CollapseMenuIcon.tsx new file mode 100644 index 0000000..d1dcad3 --- /dev/null +++ b/src/assets/icons/CollapseMenuIcon.tsx @@ -0,0 +1,31 @@ +import { Box } from "@mui/material"; + + +interface Props { + height: string; + width: string; + color: string; + transform: string; +} + +export default function CollapseMenuIcon({ height, width, color, transform }: Props) { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/ContactBookIcon.tsx b/src/assets/icons/ContactBookIcon.tsx new file mode 100644 index 0000000..174d54e --- /dev/null +++ b/src/assets/icons/ContactBookIcon.tsx @@ -0,0 +1,34 @@ +import { Box } from "@mui/material"; + + +interface Props { + height: string; + width: string; + color: string; +} + +export default function ContactBookIcon({ height, width,color }: Props) { + + return ( + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/ContactFormIcon/AddressIcon.tsx b/src/assets/icons/ContactFormIcon/AddressIcon.tsx new file mode 100644 index 0000000..9918ee4 --- /dev/null +++ b/src/assets/icons/ContactFormIcon/AddressIcon.tsx @@ -0,0 +1,40 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function AddressIcon({ color }: Props) { + return ( + + + + + + + ); +} diff --git a/src/assets/icons/ContactFormIcon/EmailIcon.tsx b/src/assets/icons/ContactFormIcon/EmailIcon.tsx new file mode 100644 index 0000000..ad09e98 --- /dev/null +++ b/src/assets/icons/ContactFormIcon/EmailIcon.tsx @@ -0,0 +1,40 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function EmailIcon({ color }: Props) { + return ( + + + + + + + ); +} diff --git a/src/assets/icons/ContactFormIcon/NameIcon.tsx b/src/assets/icons/ContactFormIcon/NameIcon.tsx new file mode 100644 index 0000000..8fafed2 --- /dev/null +++ b/src/assets/icons/ContactFormIcon/NameIcon.tsx @@ -0,0 +1,45 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function NameIcon({ color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/ContactFormIcon/PhoneIcon.tsx b/src/assets/icons/ContactFormIcon/PhoneIcon.tsx new file mode 100644 index 0000000..33266a5 --- /dev/null +++ b/src/assets/icons/ContactFormIcon/PhoneIcon.tsx @@ -0,0 +1,34 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function PhoneIcon({ color }: Props) { + return ( + + + + + + ); +} diff --git a/src/assets/icons/ContactFormIcon/TextIcon.tsx b/src/assets/icons/ContactFormIcon/TextIcon.tsx new file mode 100644 index 0000000..b272b37 --- /dev/null +++ b/src/assets/icons/ContactFormIcon/TextIcon.tsx @@ -0,0 +1,70 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function TextIcon({ color }: Props) { + return ( + + + + + + + + + + + + ); +} diff --git a/src/assets/icons/ContactFormIcon/supplementIcon.tsx b/src/assets/icons/ContactFormIcon/supplementIcon.tsx new file mode 100644 index 0000000..8ffb437 --- /dev/null +++ b/src/assets/icons/ContactFormIcon/supplementIcon.tsx @@ -0,0 +1,34 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function SupplementIcon({ color }: Props) { + return ( + + + + + + ); +} diff --git a/src/assets/icons/CopyIcon.tsx b/src/assets/icons/CopyIcon.tsx new file mode 100644 index 0000000..cf63279 --- /dev/null +++ b/src/assets/icons/CopyIcon.tsx @@ -0,0 +1,49 @@ +import { Box, useTheme } from "@mui/material"; + +interface Props { + color?: string; + bgcolor?: string; + marL?: string; +} + +export default function CopyIcon({ color, bgcolor, marL }: Props) { + const theme = useTheme(); + + return ( + + + + + + + ); +} diff --git a/src/assets/icons/CountIcon.tsx b/src/assets/icons/CountIcon.tsx new file mode 100644 index 0000000..ebbcbc8 --- /dev/null +++ b/src/assets/icons/CountIcon.tsx @@ -0,0 +1,11 @@ +export default function CountIcon() { + return ( + + + + + ); +} diff --git a/src/assets/icons/CropIcon.tsx b/src/assets/icons/CropIcon.tsx new file mode 100644 index 0000000..16b098a --- /dev/null +++ b/src/assets/icons/CropIcon.tsx @@ -0,0 +1,10 @@ +import { FC, SVGProps } from "react"; + +export const CropIcon: FC> = (props) => ( + + + + + + +); diff --git a/src/assets/icons/CrossedEyeIcon.tsx b/src/assets/icons/CrossedEyeIcon.tsx new file mode 100644 index 0000000..c9021ca --- /dev/null +++ b/src/assets/icons/CrossedEyeIcon.tsx @@ -0,0 +1,51 @@ +import { FC, SVGProps } from "react"; + +export const CrossedEyeIcon: FC> = (props) => { + return ( + + + + + + + + + ); +}; diff --git a/src/assets/icons/DomenIcon.tsx b/src/assets/icons/DomenIcon.tsx new file mode 100644 index 0000000..cf8512f --- /dev/null +++ b/src/assets/icons/DomenIcon.tsx @@ -0,0 +1,52 @@ +import { Box, useTheme } from "@mui/material"; + +interface Props { + color: string; + bgcolor: string; +} + +export default function DomenIcon({ color, bgcolor }: Props) { + const theme = useTheme(); + + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/EmojiIocns.tsx b/src/assets/icons/EmojiIocns.tsx new file mode 100644 index 0000000..ba654c6 --- /dev/null +++ b/src/assets/icons/EmojiIocns.tsx @@ -0,0 +1,28 @@ +import { FC, SVGProps } from "react"; + +export const EmojiIcons: FC> = (props) => ( + + + + + + +); diff --git a/src/assets/icons/ExpandIcon.tsx b/src/assets/icons/ExpandIcon.tsx new file mode 100644 index 0000000..30960d2 --- /dev/null +++ b/src/assets/icons/ExpandIcon.tsx @@ -0,0 +1,17 @@ +import { useTheme } from "@mui/material"; + + +interface Props { + isExpanded?: boolean; +} + +export default function ExpandIcon({ isExpanded }: Props) { + const theme = useTheme(); + + return ( + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/ExpandLessIconBG.tsx b/src/assets/icons/ExpandLessIconBG.tsx new file mode 100644 index 0000000..c07f77a --- /dev/null +++ b/src/assets/icons/ExpandLessIconBG.tsx @@ -0,0 +1,37 @@ +import { useTheme, SxProps, Box } from "@mui/material"; + +interface Props { + sx?: SxProps; +} + +export default function ExpandIcon({ sx }: Props) { + const theme = useTheme(); + + return ( + + + + + + + ); +} diff --git a/src/assets/icons/EyeIcon.tsx b/src/assets/icons/EyeIcon.tsx new file mode 100644 index 0000000..e2d31ec --- /dev/null +++ b/src/assets/icons/EyeIcon.tsx @@ -0,0 +1,23 @@ +import { Box, useTheme } from "@mui/material"; + + +export default function EyeIcon() { + const theme = useTheme(); + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/FlowArrowIcon.tsx b/src/assets/icons/FlowArrowIcon.tsx new file mode 100644 index 0000000..20ddafc --- /dev/null +++ b/src/assets/icons/FlowArrowIcon.tsx @@ -0,0 +1,30 @@ +import { Box } from "@mui/material"; + + +interface Props { + height: string; + width: string; + color: string; +} + +export default function FlowArrowIcon({ height, width, color }: Props) { + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/Frame 1171274368.svg b/src/assets/icons/Frame 1171274368.svg new file mode 100644 index 0000000..d0db607 --- /dev/null +++ b/src/assets/icons/Frame 1171274368.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/GearIcon.tsx b/src/assets/icons/GearIcon.tsx new file mode 100644 index 0000000..aa45141 --- /dev/null +++ b/src/assets/icons/GearIcon.tsx @@ -0,0 +1,29 @@ +import { Box } from "@mui/material"; + + +interface Props { + height: string; + width: string; + color: string; +} + +export default function GearIcon({ height, width, color }: Props) { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/IconPlus.tsx b/src/assets/icons/IconPlus.tsx new file mode 100644 index 0000000..d4c3149 --- /dev/null +++ b/src/assets/icons/IconPlus.tsx @@ -0,0 +1,34 @@ +import { IconButton } from "@mui/material"; + +export default function IconPlus() { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/ImageAddIcons.tsx b/src/assets/icons/ImageAddIcons.tsx new file mode 100644 index 0000000..b166442 --- /dev/null +++ b/src/assets/icons/ImageAddIcons.tsx @@ -0,0 +1,24 @@ +import { FC, SVGProps } from "react"; + +export const ImageAddIcons: FC> = (props) => ( + + + + + +); diff --git a/src/assets/icons/Info.tsx b/src/assets/icons/Info.tsx new file mode 100644 index 0000000..d2ef527 --- /dev/null +++ b/src/assets/icons/Info.tsx @@ -0,0 +1,46 @@ +import { IconButton, SxProps } from "@mui/material"; + +type InfoProps = { + width?: number; + height?: number; + sx?: SxProps; + onClick?: any; + className?: string +}; + +export default function Info({ width = 20, height = 20, sx, onClick, className }: InfoProps) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/InfoIcon.tsx b/src/assets/icons/InfoIcon.tsx new file mode 100644 index 0000000..3324846 --- /dev/null +++ b/src/assets/icons/InfoIcon.tsx @@ -0,0 +1,45 @@ +import { Box, useTheme } from "@mui/material"; + +import type { SxProps } from "@mui/material"; + +type InfoIconProps = { + sx?: SxProps; +}; + +export default function InfoIcon({ sx }: InfoIconProps) { + const theme = useTheme(); + + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/InstallQuizIcon/LDownButton.tsx b/src/assets/icons/InstallQuizIcon/LDownButton.tsx new file mode 100644 index 0000000..a306f1d --- /dev/null +++ b/src/assets/icons/InstallQuizIcon/LDownButton.tsx @@ -0,0 +1,30 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function LDownButton({ color }: Props) { + + return ( + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/InstallQuizIcon/RDownButton.tsx b/src/assets/icons/InstallQuizIcon/RDownButton.tsx new file mode 100644 index 0000000..559b63b --- /dev/null +++ b/src/assets/icons/InstallQuizIcon/RDownButton.tsx @@ -0,0 +1,30 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function RDownButton({ color }: Props) { + + return ( + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/LayoutCenteredIcon.tsx b/src/assets/icons/LayoutCenteredIcon.tsx new file mode 100644 index 0000000..2c587a3 --- /dev/null +++ b/src/assets/icons/LayoutCenteredIcon.tsx @@ -0,0 +1,26 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function LayoutCenteredIcon({ color }: Props) { + + return ( + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/LayoutExpandedIcon.tsx b/src/assets/icons/LayoutExpandedIcon.tsx new file mode 100644 index 0000000..4178924 --- /dev/null +++ b/src/assets/icons/LayoutExpandedIcon.tsx @@ -0,0 +1,26 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function LayoutExpandedIcon({ color }: Props) { + + return ( + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/LayoutIcon.tsx b/src/assets/icons/LayoutIcon.tsx new file mode 100644 index 0000000..fde94cc --- /dev/null +++ b/src/assets/icons/LayoutIcon.tsx @@ -0,0 +1,33 @@ +import { Box, useTheme } from "@mui/material"; + + +interface Props { + bgcolor?: string; + height: string; + width: string; + color: string; +} + +export default function LayoutIconOld({ bgcolor, height, width, color }: Props) { + const theme = useTheme(); + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/LayoutIconBig.tsx b/src/assets/icons/LayoutIconBig.tsx new file mode 100644 index 0000000..5326f29 --- /dev/null +++ b/src/assets/icons/LayoutIconBig.tsx @@ -0,0 +1,33 @@ +import { Box, useTheme } from "@mui/material"; + + +interface Props { + bgcolor?: string; + height: string; + width: string; + color: string; +} + +export default function LayoutIconBig({ bgcolor, height, width, color }: Props) { + const theme = useTheme(); + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/LayoutStandartIcon.tsx b/src/assets/icons/LayoutStandartIcon.tsx new file mode 100644 index 0000000..feaaf90 --- /dev/null +++ b/src/assets/icons/LayoutStandartIcon.tsx @@ -0,0 +1,27 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function LayoutStandartIcon({ color }: Props) { + + return ( + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/LinkIcon.tsx b/src/assets/icons/LinkIcon.tsx new file mode 100644 index 0000000..303779f --- /dev/null +++ b/src/assets/icons/LinkIcon.tsx @@ -0,0 +1,31 @@ +import { Box, useTheme } from "@mui/material"; + + +interface Props { + color: string; + bgcolor: string; +} + +export default function LinkIcon({ color, bgcolor }: Props) { + const theme = useTheme(); + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/LogoutIcon.tsx b/src/assets/icons/LogoutIcon.tsx new file mode 100644 index 0000000..38ea313 --- /dev/null +++ b/src/assets/icons/LogoutIcon.tsx @@ -0,0 +1,14 @@ +import { useTheme } from "@mui/material"; + + +export default function LogoutIcon() { + const theme = useTheme(); + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/MegaphoneIcon.tsx b/src/assets/icons/MegaphoneIcon.tsx new file mode 100644 index 0000000..5e28a03 --- /dev/null +++ b/src/assets/icons/MegaphoneIcon.tsx @@ -0,0 +1,29 @@ +import { Box } from "@mui/material"; + + +interface Props { + height: string; + width: string; + color: string; +} + +export default function MegaphoneIcon({ height, width, color }: Props) { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/MenuIcon.tsx b/src/assets/icons/MenuIcon.tsx new file mode 100644 index 0000000..64567cd --- /dev/null +++ b/src/assets/icons/MenuIcon.tsx @@ -0,0 +1,31 @@ +export default function MenuIcon() { + return ( + + + + + + + + + + ); +} diff --git a/src/assets/icons/MobilePhoneIcon.tsx b/src/assets/icons/MobilePhoneIcon.tsx new file mode 100644 index 0000000..ea56829 --- /dev/null +++ b/src/assets/icons/MobilePhoneIcon.tsx @@ -0,0 +1,30 @@ +import { Box, useTheme } from "@mui/material"; + + +interface Props { + bgcolor: string; +} + +export default function MobilePhoneIcon({ bgcolor }: Props) { + const theme = useTheme(); + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/NumberThree.tsx b/src/assets/icons/NumberThree.tsx new file mode 100644 index 0000000..d363e8b --- /dev/null +++ b/src/assets/icons/NumberThree.tsx @@ -0,0 +1,26 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function NumberThree({color}:Props) { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/NumberTwo.tsx b/src/assets/icons/NumberTwo.tsx new file mode 100644 index 0000000..cfd7d8d --- /dev/null +++ b/src/assets/icons/NumberTwo.tsx @@ -0,0 +1,26 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function NumberTwo({color}:Props) { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/OneIconBorder.tsx b/src/assets/icons/OneIconBorder.tsx new file mode 100644 index 0000000..5f51bd2 --- /dev/null +++ b/src/assets/icons/OneIconBorder.tsx @@ -0,0 +1,26 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; +} + +export default function OneIconBorder({color}:Props) { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/PenaLogoIcon.tsx b/src/assets/icons/PenaLogoIcon.tsx new file mode 100644 index 0000000..a52a052 --- /dev/null +++ b/src/assets/icons/PenaLogoIcon.tsx @@ -0,0 +1,67 @@ +import { FC, SVGProps } from "react"; + +export const PenaLogoIcon: FC> = (props) => ( + + + + + + + + + + + + + + + + + + + +); diff --git a/src/assets/icons/PencilCircleIcon.tsx b/src/assets/icons/PencilCircleIcon.tsx new file mode 100644 index 0000000..915211e --- /dev/null +++ b/src/assets/icons/PencilCircleIcon.tsx @@ -0,0 +1,32 @@ +import { Box } from "@mui/material"; + + +interface Props { + height: string; + width: string; + color: string; +} + +export default function PencilCircleIcon({ height, width, color }: Props) { + + return ( + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/PencilIcon.tsx b/src/assets/icons/PencilIcon.tsx new file mode 100644 index 0000000..4ec391a --- /dev/null +++ b/src/assets/icons/PencilIcon.tsx @@ -0,0 +1,23 @@ +import { Box, useTheme } from "@mui/material"; + + +export default function PencilIcon() { + const theme = useTheme(); + + return ( + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/Plus.tsx b/src/assets/icons/Plus.tsx new file mode 100644 index 0000000..f0238f1 --- /dev/null +++ b/src/assets/icons/Plus.tsx @@ -0,0 +1,19 @@ +import { IconButton } from "@mui/material"; + +export default function Plus() { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/PuzzlePieceIcon.tsx b/src/assets/icons/PuzzlePieceIcon.tsx new file mode 100644 index 0000000..137d6e8 --- /dev/null +++ b/src/assets/icons/PuzzlePieceIcon.tsx @@ -0,0 +1,28 @@ +import { Box } from "@mui/material"; + + +interface Props { + height: string; + width: string; + color: string; +} + +export default function PuzzlePieceIcon({ height, width, color }: Props) { + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/QuestionIcon.tsx b/src/assets/icons/QuestionIcon.tsx new file mode 100644 index 0000000..44aa33a --- /dev/null +++ b/src/assets/icons/QuestionIcon.tsx @@ -0,0 +1,30 @@ +import { Box } from "@mui/material"; + + +interface Props { + height: string; + width: string; + color: string; +} + +export default function QuestionIcon({ height, width ,color}: Props) { + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/QuestionsMapIcon.tsx b/src/assets/icons/QuestionsMapIcon.tsx new file mode 100644 index 0000000..2bbe9b8 --- /dev/null +++ b/src/assets/icons/QuestionsMapIcon.tsx @@ -0,0 +1,63 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function QuestionsMapIcon({ color = "#7E2AEA" }: Props) { + return ( + + + + + + + + + + ); +} diff --git a/src/assets/icons/ResetIcon.tsx b/src/assets/icons/ResetIcon.tsx new file mode 100644 index 0000000..49d2ce1 --- /dev/null +++ b/src/assets/icons/ResetIcon.tsx @@ -0,0 +1,33 @@ +import { CSSProperties, FC } from "react"; + +interface Iporps { + style?: CSSProperties; + onClick?: () => void; +} + +export const ResetIcon: FC = ({ style, onClick }) => ( + + + + +); diff --git a/src/assets/icons/ResizeIcon.tsx b/src/assets/icons/ResizeIcon.tsx new file mode 100644 index 0000000..daeb81c --- /dev/null +++ b/src/assets/icons/ResizeIcon.tsx @@ -0,0 +1,35 @@ +import { Box, SxProps, Theme } from "@mui/material"; + +interface Props { + sx?: SxProps; +} + +export default function ResizeIcon({ sx }: Props) { + return ( + + + + + + ); +} diff --git a/src/assets/icons/SearchIcon.tsx b/src/assets/icons/SearchIcon.tsx new file mode 100644 index 0000000..7d0a933 --- /dev/null +++ b/src/assets/icons/SearchIcon.tsx @@ -0,0 +1,22 @@ +import { Box } from "@mui/material"; + + +export default function SearchIcon() { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/SendIcon.tsx b/src/assets/icons/SendIcon.tsx new file mode 100644 index 0000000..6dc4705 --- /dev/null +++ b/src/assets/icons/SendIcon.tsx @@ -0,0 +1,12 @@ + + +export default function SendIcon() { + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/ShareNetwork.svg b/src/assets/icons/ShareNetwork.svg new file mode 100644 index 0000000..d19f6ba --- /dev/null +++ b/src/assets/icons/ShareNetwork.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/icons/TagIcon.tsx b/src/assets/icons/TagIcon.tsx new file mode 100644 index 0000000..5810ce0 --- /dev/null +++ b/src/assets/icons/TagIcon.tsx @@ -0,0 +1,29 @@ +import { Box } from "@mui/material"; + + +interface Props { + height: string; + width: string; + color: string; +} + +export default function TagIcon({ height, width, color }: Props) { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/TrashIcon.tsx b/src/assets/icons/TrashIcon.tsx new file mode 100644 index 0000000..1e1ec02 --- /dev/null +++ b/src/assets/icons/TrashIcon.tsx @@ -0,0 +1,48 @@ +export default function TrashIcon() { + return ( + + + + + + + + + ); +} diff --git a/src/assets/icons/Unsplash.svg b/src/assets/icons/Unsplash.svg new file mode 100644 index 0000000..d7f14de --- /dev/null +++ b/src/assets/icons/Unsplash.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icons/UploadIcon.tsx b/src/assets/icons/UploadIcon.tsx new file mode 100644 index 0000000..46c386c --- /dev/null +++ b/src/assets/icons/UploadIcon.tsx @@ -0,0 +1,24 @@ +import { Box, useTheme } from "@mui/material"; + + +export default function UploadIcon() { + const theme = useTheme(); + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/VkIcon.tsx b/src/assets/icons/VkIcon.tsx new file mode 100644 index 0000000..c6fdebc --- /dev/null +++ b/src/assets/icons/VkIcon.tsx @@ -0,0 +1,39 @@ +import { Box, useTheme } from "@mui/material"; + +interface Props { + color: string; + bgcolor: string; +} + +export default function VkIcon({ color, bgcolor }: Props) { + const theme = useTheme(); + + return ( + + + + + + ); +} diff --git a/src/assets/icons/VkIconButton.tsx b/src/assets/icons/VkIconButton.tsx new file mode 100644 index 0000000..7384ce2 --- /dev/null +++ b/src/assets/icons/VkIconButton.tsx @@ -0,0 +1,25 @@ +import { Box, useTheme } from "@mui/material"; + + +interface Props { + color?: string; +} + +export default function VkIconButton({ color }: Props) { + const theme = useTheme(); + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/WalletIcon.tsx b/src/assets/icons/WalletIcon.tsx new file mode 100644 index 0000000..3265109 --- /dev/null +++ b/src/assets/icons/WalletIcon.tsx @@ -0,0 +1,32 @@ +import { Box } from "@mui/material"; + + +interface Props { + color: string; + bgcolor: string; +} + +export default function WalletIcon({ color, bgcolor }: Props) { + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/checked.svg b/src/assets/icons/checked.svg new file mode 100644 index 0000000..17e59b0 --- /dev/null +++ b/src/assets/icons/checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/listChecks.svg b/src/assets/icons/listChecks.svg new file mode 100644 index 0000000..506f040 --- /dev/null +++ b/src/assets/icons/listChecks.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icons/messagIcon.tsx b/src/assets/icons/messagIcon.tsx new file mode 100644 index 0000000..b0af074 --- /dev/null +++ b/src/assets/icons/messagIcon.tsx @@ -0,0 +1,15 @@ +import { FC, SVGProps } from "react"; + +export const MessageIcon: FC> = (props) => ( + + + + + +); diff --git a/src/assets/icons/plus.svg b/src/assets/icons/plus.svg new file mode 100644 index 0000000..cd416b7 --- /dev/null +++ b/src/assets/icons/plus.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/qrIcon.tsx b/src/assets/icons/qrIcon.tsx new file mode 100644 index 0000000..a187f2d --- /dev/null +++ b/src/assets/icons/qrIcon.tsx @@ -0,0 +1,75 @@ +import { Box, useTheme } from "@mui/material"; + +export default function QRIcon() { + const theme = useTheme(); + + return ( + + + + + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/ArrowDownIcon.tsx b/src/assets/icons/questionsPage/ArrowDownIcon.tsx new file mode 100644 index 0000000..861571e --- /dev/null +++ b/src/assets/icons/questionsPage/ArrowDownIcon.tsx @@ -0,0 +1,13 @@ +import { FC, SVGProps } from "react"; + +export const ArrowDownIcon: FC> = (props) => ( + + + +); diff --git a/src/assets/icons/questionsPage/CopyIcon.tsx b/src/assets/icons/questionsPage/CopyIcon.tsx new file mode 100644 index 0000000..4f4a691 --- /dev/null +++ b/src/assets/icons/questionsPage/CopyIcon.tsx @@ -0,0 +1,20 @@ +import { FC, SVGProps } from "react"; + +export const CopyIcon: FC> = (props) => ( + + + + +); diff --git a/src/assets/icons/questionsPage/DoubleArrowRight.tsx b/src/assets/icons/questionsPage/DoubleArrowRight.tsx new file mode 100644 index 0000000..a7b7306 --- /dev/null +++ b/src/assets/icons/questionsPage/DoubleArrowRight.tsx @@ -0,0 +1,20 @@ +import { FC, SVGProps } from "react"; + +export const DoubleArrowRight: FC> = (props) => ( + + + + +); diff --git a/src/assets/icons/questionsPage/DoubleTick.tsx b/src/assets/icons/questionsPage/DoubleTick.tsx new file mode 100644 index 0000000..cac66c3 --- /dev/null +++ b/src/assets/icons/questionsPage/DoubleTick.tsx @@ -0,0 +1,13 @@ +import { FC, SVGProps } from "react"; + +export const DoubleTick: FC> = (props) => ( + + + +); diff --git a/src/assets/icons/questionsPage/FlagIcon.tsx b/src/assets/icons/questionsPage/FlagIcon.tsx new file mode 100644 index 0000000..b758566 --- /dev/null +++ b/src/assets/icons/questionsPage/FlagIcon.tsx @@ -0,0 +1,46 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function FlagIcon({ color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/FormatIcon1.tsx b/src/assets/icons/questionsPage/FormatIcon1.tsx new file mode 100644 index 0000000..9eaf396 --- /dev/null +++ b/src/assets/icons/questionsPage/FormatIcon1.tsx @@ -0,0 +1,78 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function FormatIcon1({ color }: Props) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/FormatIcon2.tsx b/src/assets/icons/questionsPage/FormatIcon2.tsx new file mode 100644 index 0000000..5ea18a1 --- /dev/null +++ b/src/assets/icons/questionsPage/FormatIcon2.tsx @@ -0,0 +1,70 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function FormatIcon2({ color }: Props) { + return ( + + + + + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/OneIcon.tsx b/src/assets/icons/questionsPage/OneIcon.tsx new file mode 100644 index 0000000..64853a8 --- /dev/null +++ b/src/assets/icons/questionsPage/OneIcon.tsx @@ -0,0 +1,18 @@ +import { Box } from "@mui/material"; +import { FC, SVGProps } from "react"; + +export const OneIcon: FC> = (props) => ( + + + + +); diff --git a/src/assets/icons/questionsPage/PointsIcon.tsx b/src/assets/icons/questionsPage/PointsIcon.tsx new file mode 100644 index 0000000..a4dcb4d --- /dev/null +++ b/src/assets/icons/questionsPage/PointsIcon.tsx @@ -0,0 +1,31 @@ +import { Box } from "@mui/material"; +import { FC, SVGProps } from "react"; + +export const PointsIcon: FC> = (props) => ( + + + + + + + + +); diff --git a/src/assets/icons/questionsPage/ProportionsIcon11.tsx b/src/assets/icons/questionsPage/ProportionsIcon11.tsx new file mode 100644 index 0000000..f4dbb5a --- /dev/null +++ b/src/assets/icons/questionsPage/ProportionsIcon11.tsx @@ -0,0 +1,44 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function ProportionsIcon11({ color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/ProportionsIcon12.tsx b/src/assets/icons/questionsPage/ProportionsIcon12.tsx new file mode 100644 index 0000000..694210f --- /dev/null +++ b/src/assets/icons/questionsPage/ProportionsIcon12.tsx @@ -0,0 +1,45 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function ProportionsIcon12({ color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/ProportionsIcon21.tsx b/src/assets/icons/questionsPage/ProportionsIcon21.tsx new file mode 100644 index 0000000..7e15d3c --- /dev/null +++ b/src/assets/icons/questionsPage/ProportionsIcon21.tsx @@ -0,0 +1,44 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function ProportionsIcon21({ color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/StarIconMini.tsx b/src/assets/icons/questionsPage/StarIconMini.tsx new file mode 100644 index 0000000..19cde6c --- /dev/null +++ b/src/assets/icons/questionsPage/StarIconMini.tsx @@ -0,0 +1,35 @@ +import { Box } from "@mui/material"; + +import type { SxProps } from "@mui/material"; + +interface Props { + color: string; + width?: number; + sx?: SxProps; +} + +export default function StarIconMini({ color, width = 30, sx }: Props) { + return ( + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/VectorQuestions.tsx b/src/assets/icons/questionsPage/VectorQuestions.tsx new file mode 100644 index 0000000..98a2849 --- /dev/null +++ b/src/assets/icons/questionsPage/VectorQuestions.tsx @@ -0,0 +1,20 @@ +import { FC, SVGProps } from "react"; + +export const VectorQuestions: FC> = (props) => ( + + + + +); diff --git a/src/assets/icons/questionsPage/VideofileIcon.tsx b/src/assets/icons/questionsPage/VideofileIcon.tsx new file mode 100644 index 0000000..fb4a329 --- /dev/null +++ b/src/assets/icons/questionsPage/VideofileIcon.tsx @@ -0,0 +1,14 @@ +import { FC, SVGProps } from "react"; + +export const VideofileIcon: FC> = (props) => ( + + + + +); diff --git a/src/assets/icons/questionsPage/addAnswer.tsx b/src/assets/icons/questionsPage/addAnswer.tsx new file mode 100644 index 0000000..3bb950d --- /dev/null +++ b/src/assets/icons/questionsPage/addAnswer.tsx @@ -0,0 +1,34 @@ +import { Box, SxProps, Theme } from "@mui/material"; + +interface Props { + color: string; + sx?: SxProps; +} +export default function AddAnswer({ color, sx }: Props) { + return ( + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/addEmoji.tsx b/src/assets/icons/questionsPage/addEmoji.tsx new file mode 100644 index 0000000..4d88f66 --- /dev/null +++ b/src/assets/icons/questionsPage/addEmoji.tsx @@ -0,0 +1,62 @@ +import { Box } from "@mui/material"; + +// interface Props { +// color: string; +// } + +export default function AddEmoji() { + return ( + + + + + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/addImage.tsx b/src/assets/icons/questionsPage/addImage.tsx new file mode 100644 index 0000000..3842072 --- /dev/null +++ b/src/assets/icons/questionsPage/addImage.tsx @@ -0,0 +1,56 @@ +import { Box, SxProps } from "@mui/material"; +import { FC } from "react"; + +interface Iprops { + onClick?: () => void; + sx?: SxProps; +} + +const AddImage: FC = ({ onClick, sx }) => { + return ( + + + + + + + + + + + ); +}; + +export default AddImage; diff --git a/src/assets/icons/questionsPage/addPlus.tsx b/src/assets/icons/questionsPage/addPlus.tsx new file mode 100644 index 0000000..3477eda --- /dev/null +++ b/src/assets/icons/questionsPage/addPlus.tsx @@ -0,0 +1,27 @@ +import { Box } from "@mui/material"; + + +// interface Props { +// color: string; +// } + +export default function AddPlus() { + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/addPlusImage.tsx b/src/assets/icons/questionsPage/addPlusImage.tsx new file mode 100644 index 0000000..3d77bf4 --- /dev/null +++ b/src/assets/icons/questionsPage/addPlusImage.tsx @@ -0,0 +1,30 @@ +import { FC } from "react"; + +export const AddPlusImage: FC = () => ( + + + + + + + + +); diff --git a/src/assets/icons/questionsPage/addPlusVideo.tsx b/src/assets/icons/questionsPage/addPlusVideo.tsx new file mode 100644 index 0000000..24428cb --- /dev/null +++ b/src/assets/icons/questionsPage/addPlusVideo.tsx @@ -0,0 +1,20 @@ +import { FC } from "react"; + +export const AddPlusVideo: FC = () => ( + + + + + + + +); diff --git a/src/assets/icons/questionsPage/addVideofile.tsx b/src/assets/icons/questionsPage/addVideofile.tsx new file mode 100644 index 0000000..2f9a1e6 --- /dev/null +++ b/src/assets/icons/questionsPage/addVideofile.tsx @@ -0,0 +1,54 @@ +import { Box } from "@mui/material"; + +// interface Props { +// color: string; +// } + +export default function AddVideofile() { + return ( + + + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/answer.tsx b/src/assets/icons/questionsPage/answer.tsx new file mode 100644 index 0000000..df857f6 --- /dev/null +++ b/src/assets/icons/questionsPage/answer.tsx @@ -0,0 +1,54 @@ +import { Box, SxProps, Theme } from "@mui/material"; + +interface Props { + color: string; + sx?: SxProps; +} + +export default function Answer({ color, sx }: Props) { + return ( + + + + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/answerGroup.tsx b/src/assets/icons/questionsPage/answerGroup.tsx new file mode 100644 index 0000000..d23247b --- /dev/null +++ b/src/assets/icons/questionsPage/answerGroup.tsx @@ -0,0 +1,43 @@ +import { Box, SxProps, Theme } from "@mui/material"; + +interface Props { + color: string; + sx?: SxProps; +} +export default function AnswerGroup({ color, sx }: Props) { + return ( + + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/arrowLeft.tsx b/src/assets/icons/questionsPage/arrowLeft.tsx new file mode 100644 index 0000000..7e4a448 --- /dev/null +++ b/src/assets/icons/questionsPage/arrowLeft.tsx @@ -0,0 +1,25 @@ +import { Box } from "@mui/material"; + + +// interface Props { +// color: string; +// } + +export default function ArrowLeft() { + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/branching.tsx b/src/assets/icons/questionsPage/branching.tsx new file mode 100644 index 0000000..962bb99 --- /dev/null +++ b/src/assets/icons/questionsPage/branching.tsx @@ -0,0 +1,58 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function Branching({ color }: Props) { + return ( + + + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/clue.tsx b/src/assets/icons/questionsPage/clue.tsx new file mode 100644 index 0000000..67e288e --- /dev/null +++ b/src/assets/icons/questionsPage/clue.tsx @@ -0,0 +1,46 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function Clue({ color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/date.tsx b/src/assets/icons/questionsPage/date.tsx new file mode 100644 index 0000000..33f51b6 --- /dev/null +++ b/src/assets/icons/questionsPage/date.tsx @@ -0,0 +1,40 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} + +export default function Date({color, sx}: Props) { + + return ( + + + + + + + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/deleteIcon.tsx b/src/assets/icons/questionsPage/deleteIcon.tsx new file mode 100644 index 0000000..8ebd1d6 --- /dev/null +++ b/src/assets/icons/questionsPage/deleteIcon.tsx @@ -0,0 +1,24 @@ +import { Box } from "@mui/material"; +import { FC, SVGProps } from "react"; + +export const DeleteIcon: FC> = (props) => ( + + + + + + + +); diff --git a/src/assets/icons/questionsPage/download.tsx b/src/assets/icons/questionsPage/download.tsx new file mode 100644 index 0000000..3af8f80 --- /dev/null +++ b/src/assets/icons/questionsPage/download.tsx @@ -0,0 +1,31 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} + +export default function Download({color, sx}: Props) { + + return ( + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/drop_down.tsx b/src/assets/icons/questionsPage/drop_down.tsx new file mode 100644 index 0000000..ff4b0ee --- /dev/null +++ b/src/assets/icons/questionsPage/drop_down.tsx @@ -0,0 +1,34 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} + +export default function DropDown({color, sx}: Props) { + + return ( + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/emoji.tsx b/src/assets/icons/questionsPage/emoji.tsx new file mode 100644 index 0000000..497d464 --- /dev/null +++ b/src/assets/icons/questionsPage/emoji.tsx @@ -0,0 +1,30 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} + +export default function Emoji({color, sx}: Props) { + + return ( + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/enterIcon.tsx b/src/assets/icons/questionsPage/enterIcon.tsx new file mode 100644 index 0000000..b76eb3f --- /dev/null +++ b/src/assets/icons/questionsPage/enterIcon.tsx @@ -0,0 +1,27 @@ +import { FC, SVGProps } from "react"; + +export const EnterIcon: FC> = (props) => ( + + + + + +); diff --git a/src/assets/icons/questionsPage/hashtagIcon.tsx b/src/assets/icons/questionsPage/hashtagIcon.tsx new file mode 100644 index 0000000..bdae05a --- /dev/null +++ b/src/assets/icons/questionsPage/hashtagIcon.tsx @@ -0,0 +1,35 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function HashtagIcon({ color }: Props) { + return ( + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/heartIcon.tsx b/src/assets/icons/questionsPage/heartIcon.tsx new file mode 100644 index 0000000..4674358 --- /dev/null +++ b/src/assets/icons/questionsPage/heartIcon.tsx @@ -0,0 +1,36 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function HeartIcon({ color }: Props) { + return ( + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/hideIcon.tsx b/src/assets/icons/questionsPage/hideIcon.tsx new file mode 100644 index 0000000..692066e --- /dev/null +++ b/src/assets/icons/questionsPage/hideIcon.tsx @@ -0,0 +1,27 @@ +import { FC, SVGProps } from "react"; + +export const HideIcon: FC> = (props) => ( + + + + + +); diff --git a/src/assets/icons/questionsPage/image.tsx b/src/assets/icons/questionsPage/image.tsx new file mode 100644 index 0000000..536b943 --- /dev/null +++ b/src/assets/icons/questionsPage/image.tsx @@ -0,0 +1,53 @@ +import { Box, SxProps } from "@mui/material"; +import { FC } from "react"; + +interface Iprops { + onClick?: () => void; + sx?: SxProps; +} + +const Image: FC = ({ onClick, sx }) => { + return ( + + + + + + + + ); +}; + +export default Image; diff --git a/src/assets/icons/questionsPage/imgIcon.tsx b/src/assets/icons/questionsPage/imgIcon.tsx new file mode 100644 index 0000000..4f30a74 --- /dev/null +++ b/src/assets/icons/questionsPage/imgIcon.tsx @@ -0,0 +1,44 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function ImgIcon({ color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/input.tsx b/src/assets/icons/questionsPage/input.tsx new file mode 100644 index 0000000..1188c5c --- /dev/null +++ b/src/assets/icons/questionsPage/input.tsx @@ -0,0 +1,28 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} +export default function Input({color, sx}: Props) { + + return ( + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/lightbulbIcon.tsx b/src/assets/icons/questionsPage/lightbulbIcon.tsx new file mode 100644 index 0000000..04d389b --- /dev/null +++ b/src/assets/icons/questionsPage/lightbulbIcon.tsx @@ -0,0 +1,48 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function LightbulbIcon({ color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/likeIcon.tsx b/src/assets/icons/questionsPage/likeIcon.tsx new file mode 100644 index 0000000..78f7b30 --- /dev/null +++ b/src/assets/icons/questionsPage/likeIcon.tsx @@ -0,0 +1,46 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function LikeIcon({ color }: Props) { + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/options_and_pict.tsx b/src/assets/icons/questionsPage/options_and_pict.tsx new file mode 100644 index 0000000..acdde9d --- /dev/null +++ b/src/assets/icons/questionsPage/options_and_pict.tsx @@ -0,0 +1,34 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} + +export default function optionsAndPict({color, sx}: Props) { + + return ( + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/options_pict.tsx b/src/assets/icons/questionsPage/options_pict.tsx new file mode 100644 index 0000000..6e11813 --- /dev/null +++ b/src/assets/icons/questionsPage/options_pict.tsx @@ -0,0 +1,30 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} + +export default function optionsPict({color, sx}: Props) { + + return ( + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/page.tsx b/src/assets/icons/questionsPage/page.tsx new file mode 100644 index 0000000..09a9b03 --- /dev/null +++ b/src/assets/icons/questionsPage/page.tsx @@ -0,0 +1,33 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} + +export default function Page({color, sx}: Props) { + + return ( + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/plus.tsx b/src/assets/icons/questionsPage/plus.tsx new file mode 100644 index 0000000..98b7fa8 --- /dev/null +++ b/src/assets/icons/questionsPage/plus.tsx @@ -0,0 +1,32 @@ +import { Box } from "@mui/material"; + +export default function Plus() { + return ( + + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/rating.tsx b/src/assets/icons/questionsPage/rating.tsx new file mode 100644 index 0000000..43c6d5f --- /dev/null +++ b/src/assets/icons/questionsPage/rating.tsx @@ -0,0 +1,26 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} +export default function Rating({color, sx}: Props) { + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/settingIcon.tsx b/src/assets/icons/questionsPage/settingIcon.tsx new file mode 100644 index 0000000..19bbeb5 --- /dev/null +++ b/src/assets/icons/questionsPage/settingIcon.tsx @@ -0,0 +1,34 @@ +import { Box } from "@mui/material"; + +interface Props { + color?: string; +} + +export default function SettingIcon({ color }: Props) { + return ( + + + + + + ); +} diff --git a/src/assets/icons/questionsPage/slider.tsx b/src/assets/icons/questionsPage/slider.tsx new file mode 100644 index 0000000..4dbaa9b --- /dev/null +++ b/src/assets/icons/questionsPage/slider.tsx @@ -0,0 +1,28 @@ +import {Box, SxProps, Theme} from "@mui/material"; + + +interface Props { + color: string; + sx?: SxProps +} + +export default function Slider({color, sx}: Props) { + + return ( + + + + + + + ); +} \ No newline at end of file diff --git a/src/assets/icons/questionsPage/tropfyIcon.tsx b/src/assets/icons/questionsPage/tropfyIcon.tsx new file mode 100644 index 0000000..8f0ae03 --- /dev/null +++ b/src/assets/icons/questionsPage/tropfyIcon.tsx @@ -0,0 +1,64 @@ +import { Box } from "@mui/material"; + +interface Props { + color: string; +} + +export default function TropfyIcon({ color }: Props) { + return ( + + + + + + + + + + ); +} diff --git a/src/assets/icons/telegramIcon.tsx b/src/assets/icons/telegramIcon.tsx new file mode 100644 index 0000000..8e95f7a --- /dev/null +++ b/src/assets/icons/telegramIcon.tsx @@ -0,0 +1,47 @@ +import { Box, useTheme } from "@mui/material"; + +export default function TelegramIcon() { + const theme = useTheme(); + + return ( + + + + + + + + ); +} diff --git a/src/assets/icons/tiktokIcon.tsx b/src/assets/icons/tiktokIcon.tsx new file mode 100644 index 0000000..12692e6 --- /dev/null +++ b/src/assets/icons/tiktokIcon.tsx @@ -0,0 +1,33 @@ +import { Box, useTheme } from "@mui/material"; + +export default function TiktokIcon() { + const theme = useTheme(); + + return ( + + + + + + ); +} diff --git a/src/assets/icons/trash.tsx b/src/assets/icons/trash.tsx new file mode 100644 index 0000000..90f5a0b --- /dev/null +++ b/src/assets/icons/trash.tsx @@ -0,0 +1,64 @@ +import { useTheme, SxProps, Box } from "@mui/material"; + +interface Props { + sx?: SxProps; +} + +export default function Trash({ sx }: Props) { + const theme = useTheme(); + + return ( + + + + + + + + + + ); +} diff --git a/src/assets/icons/x.tsx b/src/assets/icons/x.tsx new file mode 100644 index 0000000..535f7c1 --- /dev/null +++ b/src/assets/icons/x.tsx @@ -0,0 +1,15 @@ +import { useTheme } from "@mui/material"; + + +interface Props { + width?: number; +} + +export default function PenaLogo({ width }: Props) { + const theme = useTheme(); + + return ( + + + + )} diff --git a/src/assets/quiz-creation-1.png b/src/assets/quiz-creation-1.png new file mode 100644 index 0000000..2ce7c88 Binary files /dev/null and b/src/assets/quiz-creation-1.png differ diff --git a/src/assets/quiz-creation-2.png b/src/assets/quiz-creation-2.png new file mode 100644 index 0000000..c7ecd32 Binary files /dev/null and b/src/assets/quiz-creation-2.png differ diff --git a/src/assets/quiz-template-1.png b/src/assets/quiz-template-1.png new file mode 100644 index 0000000..0703b74 Binary files /dev/null and b/src/assets/quiz-template-1.png differ diff --git a/src/assets/quiz-template-2.png b/src/assets/quiz-template-2.png new file mode 100644 index 0000000..f92c8c6 Binary files /dev/null and b/src/assets/quiz-template-2.png differ diff --git a/src/assets/quiz-template-3.png b/src/assets/quiz-template-3.png new file mode 100644 index 0000000..8bb8e8c Binary files /dev/null and b/src/assets/quiz-template-3.png differ diff --git a/src/assets/quiz-template-4.png b/src/assets/quiz-template-4.png new file mode 100644 index 0000000..11ecf89 Binary files /dev/null and b/src/assets/quiz-template-4.png differ diff --git a/src/assets/quiz-template-5.png b/src/assets/quiz-template-5.png new file mode 100644 index 0000000..4cbde4e Binary files /dev/null and b/src/assets/quiz-template-5.png differ diff --git a/src/assets/quiz-template-6.png b/src/assets/quiz-template-6.png new file mode 100644 index 0000000..0308e86 Binary files /dev/null and b/src/assets/quiz-template-6.png differ diff --git a/src/constants/base.ts b/src/constants/base.ts new file mode 100644 index 0000000..77feb97 --- /dev/null +++ b/src/constants/base.ts @@ -0,0 +1,29 @@ +import type { QuizQuestionBase, QuestionBranchingRuleMain } from "../model/questionTypes/shared"; + + +export const QUIZ_QUESTION_BASE: Omit = { + quizId: 0, + description: "", + page: 0, + title: "", + expanded: true, + openedModalSettings: false, + deleted: false, + deleteTimeoutId: 0, + content: { + id: "", + hint: { + text: "", + video: "", + }, + rule: { + children: [], + main: [] as QuestionBranchingRuleMain[], + parentId: "", + default: "" + }, + back: "", + originalBack: "", + autofill: false, + }, +}; diff --git a/src/constants/date.ts b/src/constants/date.ts new file mode 100644 index 0000000..6455fac --- /dev/null +++ b/src/constants/date.ts @@ -0,0 +1,16 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionDate } from "../model/questionTypes/date"; + +export const QUIZ_QUESTION_DATE: Omit = { + ...QUIZ_QUESTION_BASE, + type: "date", + content: { + ...QUIZ_QUESTION_BASE.content, + required: false, + innerNameCheck: false, + innerName: "", + dateRange: false, + time: false, + }, +}; diff --git a/src/constants/default.ts b/src/constants/default.ts new file mode 100644 index 0000000..f3bcd48 --- /dev/null +++ b/src/constants/default.ts @@ -0,0 +1,30 @@ +import { QuestionType } from "@model/question/question"; +import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; +import { QUIZ_QUESTION_DATE } from "./date"; +import { QUIZ_QUESTION_EMOJI } from "./emoji"; +import { QUIZ_QUESTION_FILE } from "./file"; +import { QUIZ_QUESTION_IMAGES } from "./images"; +import { QUIZ_QUESTION_NUMBER } from "./number"; +import { QUIZ_QUESTION_PAGE } from "./page"; +import { QUIZ_QUESTION_RATING } from "./rating"; +import { QUIZ_QUESTION_SELECT } from "./select"; +import { QUIZ_QUESTION_TEXT } from "./text"; +import { QUIZ_QUESTION_VARIANT } from "./variant"; +import { QUIZ_QUESTION_VARIMG } from "./varimg"; +import { QUIZ_QUESTION_RESULT } from "./result"; + + +export const defaultQuestionByType: Record> = { + "date": QUIZ_QUESTION_DATE, + "emoji": QUIZ_QUESTION_EMOJI, + "file": QUIZ_QUESTION_FILE, + "images": QUIZ_QUESTION_IMAGES, + "number": QUIZ_QUESTION_NUMBER, + "page": QUIZ_QUESTION_PAGE, + "rating": QUIZ_QUESTION_RATING, + "select": QUIZ_QUESTION_SELECT, + "text": QUIZ_QUESTION_TEXT, + "variant": QUIZ_QUESTION_VARIANT, + "varimg": QUIZ_QUESTION_VARIMG, + "result": QUIZ_QUESTION_RESULT, +} as const; diff --git a/src/constants/emoji.ts b/src/constants/emoji.ts new file mode 100644 index 0000000..fea13ba --- /dev/null +++ b/src/constants/emoji.ts @@ -0,0 +1,26 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionEmoji } from "../model/questionTypes/emoji"; +import { nanoid } from "nanoid"; + +export const QUIZ_QUESTION_EMOJI: Omit = { + ...QUIZ_QUESTION_BASE, + type: "emoji", + content: { + ...QUIZ_QUESTION_BASE.content, + multi: false, + own: false, + innerNameCheck: false, + innerName: "", + required: false, + variants: [ + { + id: nanoid(), + answer: "", + extendedText: "", + hints: "", + originalImageUrl: "", + }, + ], + }, +}; diff --git a/src/constants/file.ts b/src/constants/file.ts new file mode 100644 index 0000000..8e36b23 --- /dev/null +++ b/src/constants/file.ts @@ -0,0 +1,15 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionFile } from "../model/questionTypes/file"; + +export const QUIZ_QUESTION_FILE: Omit = { + ...QUIZ_QUESTION_BASE, + type: "file", + content: { + ...QUIZ_QUESTION_BASE.content, + required: false, + innerNameCheck: false, + innerName: "", + type: "picture", + }, +}; diff --git a/src/constants/images.ts b/src/constants/images.ts new file mode 100644 index 0000000..dd92dc9 --- /dev/null +++ b/src/constants/images.ts @@ -0,0 +1,30 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionImages } from "../model/questionTypes/images"; +import { nanoid } from "nanoid"; + +export const QUIZ_QUESTION_IMAGES: Omit = { + ...QUIZ_QUESTION_BASE, + type: "images", + content: { + ...QUIZ_QUESTION_BASE.content, + own: false, + multi: false, + xy: "1:1", + innerNameCheck: false, + innerName: "", + large: false, + format: "carousel", + required: false, + variants: [ + { + id: nanoid(), + answer: "", + extendedText: "", + originalImageUrl: "", + hints: "" + }, + ], + largeCheck: false, + }, +}; diff --git a/src/constants/number.ts b/src/constants/number.ts new file mode 100644 index 0000000..96cbd5a --- /dev/null +++ b/src/constants/number.ts @@ -0,0 +1,21 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionNumber } from "../model/questionTypes/number"; + +export const QUIZ_QUESTION_NUMBER: Omit = { + ...QUIZ_QUESTION_BASE, + type: "number", + content: { + ...QUIZ_QUESTION_BASE.content, + required: false, + innerNameCheck: false, + innerName: "", + range: "1—100", + defaultValue: 0, + step: 1, + steps: 5, + start: 50, + chooseRange: false, + form: "star", + }, +}; diff --git a/src/constants/page.ts b/src/constants/page.ts new file mode 100644 index 0000000..5fbb296 --- /dev/null +++ b/src/constants/page.ts @@ -0,0 +1,17 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionPage } from "../model/questionTypes/page"; + +export const QUIZ_QUESTION_PAGE: Omit = { + ...QUIZ_QUESTION_BASE, + type: "page", + content: { + ...QUIZ_QUESTION_BASE.content, + innerNameCheck: false, + innerName: "", + text: "", + picture: "", + originalPicture: "", + video: "", + }, +}; diff --git a/src/constants/rating.ts b/src/constants/rating.ts new file mode 100644 index 0000000..cef8bc2 --- /dev/null +++ b/src/constants/rating.ts @@ -0,0 +1,19 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionRating } from "../model/questionTypes/rating"; + +export const QUIZ_QUESTION_RATING: Omit = { + ...QUIZ_QUESTION_BASE, + type: "rating", + content: { + ...QUIZ_QUESTION_BASE.content, + required: false, + innerNameCheck: false, + innerName: "", + steps: 5, + ratingExpanded: false, + form: "star", + ratingNegativeDescription: "", + ratingPositiveDescription: "", + }, +}; diff --git a/src/constants/result.ts b/src/constants/result.ts new file mode 100644 index 0000000..b9f4015 --- /dev/null +++ b/src/constants/result.ts @@ -0,0 +1,17 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionResult } from "../model/questionTypes/result"; +import { nanoid } from "nanoid"; + +export const QUIZ_QUESTION_RESULT: Omit = { + ...QUIZ_QUESTION_BASE, + type: "result", + content: { + ...QUIZ_QUESTION_BASE.content, + video: "", + innerName: "", + text: "", + price: [0], + useImage: true + }, +}; diff --git a/src/constants/select.ts b/src/constants/select.ts new file mode 100644 index 0000000..ff8de24 --- /dev/null +++ b/src/constants/select.ts @@ -0,0 +1,18 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionSelect } from "../model/questionTypes/select"; +import { nanoid } from "nanoid"; + +export const QUIZ_QUESTION_SELECT: Omit = { + ...QUIZ_QUESTION_BASE, + type: "select", + content: { + ...QUIZ_QUESTION_BASE.content, + multi: false, + required: false, + innerNameCheck: false, + innerName: "", + default: "", + variants: [{ id: nanoid(), answer: "", extendedText: "", hints: "", originalImageUrl: "" }], + }, +}; diff --git a/src/constants/text.ts b/src/constants/text.ts new file mode 100644 index 0000000..a64c4f9 --- /dev/null +++ b/src/constants/text.ts @@ -0,0 +1,17 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionText } from "../model/questionTypes/text"; + +export const QUIZ_QUESTION_TEXT: Omit = { + ...QUIZ_QUESTION_BASE, + type: "text", + content: { + ...QUIZ_QUESTION_BASE.content, + placeholder: "", + innerNameCheck: false, + innerName: "", + required: false, + answerType: "single", + onlyNumbers: false, + }, +}; diff --git a/src/constants/variant.ts b/src/constants/variant.ts new file mode 100644 index 0000000..eaca719 --- /dev/null +++ b/src/constants/variant.ts @@ -0,0 +1,19 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionVariant } from "../model/questionTypes/variant"; +import { nanoid } from "nanoid"; + +export const QUIZ_QUESTION_VARIANT: Omit = { + ...QUIZ_QUESTION_BASE, + type: "variant", + content: { + ...QUIZ_QUESTION_BASE.content, + largeCheck: false, + multi: false, + own: false, + innerNameCheck: false, + required: false, + innerName: "", + variants: [{ id: nanoid(), answer: "", extendedText: "", hints: "", originalImageUrl: "" }], + }, +}; diff --git a/src/constants/varimg.ts b/src/constants/varimg.ts new file mode 100644 index 0000000..ef97133 --- /dev/null +++ b/src/constants/varimg.ts @@ -0,0 +1,19 @@ +import { QUIZ_QUESTION_BASE } from "./base"; + +import type { QuizQuestionVarImg } from "../model/questionTypes/varimg"; +import { nanoid } from "nanoid"; + +export const QUIZ_QUESTION_VARIMG: Omit = { + ...QUIZ_QUESTION_BASE, + type: "varimg", + content: { + ...QUIZ_QUESTION_BASE.content, + own: false, + innerNameCheck: false, + innerName: "", + required: false, + variants: [{ id: nanoid(), answer: "", hints: "", extendedText: "", originalImageUrl: "" }], + largeCheck: false, + replText: "", + }, +}; diff --git a/src/declare.d.ts b/src/declare.d.ts new file mode 100644 index 0000000..4997750 --- /dev/null +++ b/src/declare.d.ts @@ -0,0 +1 @@ +declare module "*.png"; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..6f14e2b --- /dev/null +++ b/src/index.css @@ -0,0 +1,29 @@ +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; +} + +@keyframes blinking { + 0% { + opacity: 100; + } + 50% { + opacity: 0; + } + 100% { + opacity: 100; + } +} + +.blink { + animation: blinking 2s infinite ; +} \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..604a3d2 --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,44 @@ +import { CssBaseline, ThemeProvider } from "@mui/material"; +import { LocalizationProvider } from "@mui/x-date-pickers"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import { ruRU } from "@mui/x-date-pickers/locales"; +import App from "./App"; +import dayjs from "dayjs"; +import "dayjs/locale/ru"; +import { SnackbarProvider } from "notistack"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import lightTheme from "./utils/themes/light"; +import { SWRConfig } from "swr"; + +dayjs.locale("ru"); + +const localeText = + ruRU.components.MuiLocalizationProvider.defaultProps.localeText; + +const root = createRoot(document.getElementById("root")!); + +root.render( + + + + + + + + + + +); diff --git a/src/model/question/copy.ts b/src/model/question/copy.ts new file mode 100644 index 0000000..834f245 --- /dev/null +++ b/src/model/question/copy.ts @@ -0,0 +1,8 @@ +export interface CopyQuestionRequest { + id: number; + quiz_id: number; +} + +export interface CopyQuestionResponse { + updated: number; +} diff --git a/src/model/question/create.ts b/src/model/question/create.ts new file mode 100644 index 0000000..89190b6 --- /dev/null +++ b/src/model/question/create.ts @@ -0,0 +1,19 @@ +import { QuestionType } from "./question"; + + +export interface CreateQuestionRequest { + /** id of quiz for what question is creating */ + quiz_id: number; + /** title of question. max length 512 */ + title: string; + /** description of question. html/text */ + description: string; + /** type of question. allow only text, select, file, variant, images, varimg, emoji, date, number, page, rating */ + type: QuestionType; + /** set true if user MUST answer this question */ + required: boolean; + /** page of question */ + page: number; + /** json serialized of question content settings */ + content: string; +} diff --git a/src/model/question/delete.ts b/src/model/question/delete.ts new file mode 100644 index 0000000..441bacf --- /dev/null +++ b/src/model/question/delete.ts @@ -0,0 +1,7 @@ +export interface DeleteQuestionRequest { + id: number; +} + +export interface DeleteQuestionResponse { + deactivated: number; +} diff --git a/src/model/question/edit.ts b/src/model/question/edit.ts new file mode 100644 index 0000000..498e652 --- /dev/null +++ b/src/model/question/edit.ts @@ -0,0 +1,29 @@ +import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; +import { QuestionType } from "./question"; + + +export interface EditQuestionRequest { + id: number; + title?: string; + desc?: string; + type?: QuestionType; + required?: boolean; + page?: number; + content: string; +} + +export interface EditQuestionResponse { + updated: number; +} + +export function questionToEditQuestionRequest(question: AnyTypedQuizQuestion): EditQuestionRequest { + return { + id: question.backendId, + title: question.title, + desc: question.description, + type: question.type, + required: "required" in question.content ? question.content.required : false, + page: question.page, + content: JSON.stringify(question.content), + }; +} diff --git a/src/model/question/getList.ts b/src/model/question/getList.ts new file mode 100644 index 0000000..a0af41b --- /dev/null +++ b/src/model/question/getList.ts @@ -0,0 +1,28 @@ +import { QuestionType, RawQuestion } from "./question"; + + +export interface GetQuestionListRequest { + /** max items on page */ + limit?: number; + /** page number */ + offset?: number; + /** start time of time period. timestamp in seconds */ + from?: number; + /** end time of time period. timestamp in seconds */ + to?: number; + /** string for fulltext search in titles of questions */ + search?: string; + /** allow only - text, select, file, variant, images, varimg, emoji, date, number, page, rating or empty string */ + type: "" | QuestionType; + /** get deleted quizes */ + deleted?: boolean; + /** get only require questions */ + required?: boolean; + /** relation to quiz */ + quiz_id?: number; +} + +export interface GetQuestionListResponse { + count: number; + items: RawQuestion[] | null; +} diff --git a/src/model/question/question.ts b/src/model/question/question.ts new file mode 100644 index 0000000..0a61942 --- /dev/null +++ b/src/model/question/question.ts @@ -0,0 +1,74 @@ +import { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; +import { defaultQuestionByType } from "../../constants/default"; +import { nanoid } from "nanoid"; + + +export type QuestionType = + | "variant" + | "images" + | "varimg" + | "emoji" + | "text" + | "select" + | "date" + | "number" + | "file" + | "page" + | "rating" + | "result"; + +/** Type that comes from server */ +export interface RawQuestion { + /** Id of created question */ + id: number; + /** relation to quiz */ + quiz_id: number; + /** title of question. max 512 length */ + title: string; + /** description of question */ + description: string; + /** status of question. allow only text, select, file, variant, images, varimg, emoji, date, number, page, rating */ + type: QuestionType; + /** user must pass this question */ + required: boolean; + /** true if question is deleted */ + deleted: boolean; + /** page if question */ + page: number; + /** serialized json of created question */ + content: string; + /** version of quiz */ + version: number; + /** array of previous versions of quiz */ + parent_ids: number[]; + created_at: string; + updated_at: string; +} + +export function rawQuestionToQuestion(rawQuestion: RawQuestion): AnyTypedQuizQuestion { + let content = defaultQuestionByType[rawQuestion.type].content; + const frontId = nanoid() + + try { + content = JSON.parse(rawQuestion.content); + if (content.id.length === 0 || content.id.length === undefined) content.id = frontId + } catch (error) { + console.warn("Cannot parse question content from string, using default content", error); + } + + return { + backendId: rawQuestion.id, + id: frontId, + description: rawQuestion.description, + page: rawQuestion.page, + quizId: rawQuestion.quiz_id, + required: rawQuestion.required, + title: rawQuestion.title, + type: rawQuestion.type, + expanded: true, + openedModalSettings: false, + deleted: false, + deleteTimeoutId: 0, + content, + } as AnyTypedQuizQuestion; +} diff --git a/src/model/questionTypes/date.ts b/src/model/questionTypes/date.ts new file mode 100644 index 0000000..651550e --- /dev/null +++ b/src/model/questionTypes/date.ts @@ -0,0 +1,25 @@ +import type { + QuizQuestionBase, + QuestionHint, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionDate extends QuizQuestionBase { + type: "date"; + content: { + id: string; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + dateRange: boolean; + time: boolean; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + }; +} diff --git a/src/model/questionTypes/emoji.ts b/src/model/questionTypes/emoji.ts new file mode 100644 index 0000000..7fa34a1 --- /dev/null +++ b/src/model/questionTypes/emoji.ts @@ -0,0 +1,29 @@ +import type { + QuizQuestionBase, + QuestionVariant, + QuestionHint, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionEmoji extends QuizQuestionBase { + type: "emoji"; + content: { + id: string; + /** Чекбокс "Можно несколько" */ + multi: boolean; + /** Чекбокс "Вариант "свой ответ"" */ + own: boolean; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + variants: QuestionVariant[]; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + }; +} diff --git a/src/model/questionTypes/file.ts b/src/model/questionTypes/file.ts new file mode 100644 index 0000000..51a748a --- /dev/null +++ b/src/model/questionTypes/file.ts @@ -0,0 +1,35 @@ +import type { + QuizQuestionBase, + QuestionHint, + QuestionBranchingRule, +} from "./shared"; + +export const UPLOAD_FILE_TYPES_MAP = { + all: "Все типы файлов", + picture: "Изображения", + video: "Видео", + audio: "Аудио", + document: "Документ", +} as const; + +export type UploadFileType = keyof typeof UPLOAD_FILE_TYPES_MAP; + +export interface QuizQuestionFile extends QuizQuestionBase { + type: "file"; + content: { + id: string; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Чекбокс "Автозаполнение адреса" */ + autofill: boolean; + type: UploadFileType; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + }; +} diff --git a/src/model/questionTypes/images.ts b/src/model/questionTypes/images.ts new file mode 100644 index 0000000..8ed2a33 --- /dev/null +++ b/src/model/questionTypes/images.ts @@ -0,0 +1,37 @@ +import type { + QuestionHint, + QuestionVariant, + QuizQuestionBase, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionImages extends QuizQuestionBase { + type: "images"; + content: { + id: string; + /** Чекбокс "Вариант "свой ответ"" */ + own: boolean; + /** Чекбокс "Можно несколько" */ + multi: boolean; + /** Пропорции */ + xy: "1:1" | "1:2" | "2:1"; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Чекбокс "Большие картинки" */ + large: boolean; + /** Форма */ + format: "carousel" | "masonry"; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + /** Варианты (картинки) */ + variants: QuestionVariant[]; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + largeCheck: boolean; + }; +} diff --git a/src/model/questionTypes/number.ts b/src/model/questionTypes/number.ts new file mode 100644 index 0000000..530db03 --- /dev/null +++ b/src/model/questionTypes/number.ts @@ -0,0 +1,35 @@ +import type { + QuizQuestionBase, + QuestionHint, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionNumber extends QuizQuestionBase { + type: "number"; + content: { + id: string; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Диапазон */ + range: string; + /** Начальное значение */ + start: number; + /** Начальное значение */ + defaultValue: number; + /** Шаг */ + step: number; + steps: number; + /** Чекбокс "Выбор диапазона (два ползунка)" */ + chooseRange: boolean; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + form: "star" | "trophie" | "flag" | "heart" | "like" | "bubble" | "hashtag"; + }; +} diff --git a/src/model/questionTypes/page.ts b/src/model/questionTypes/page.ts new file mode 100644 index 0000000..6b1d559 --- /dev/null +++ b/src/model/questionTypes/page.ts @@ -0,0 +1,25 @@ +import type { + QuizQuestionBase, + QuestionHint, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionPage extends QuizQuestionBase { + type: "page"; + content: { + id: string; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + text: string; + picture: string; + originalPicture: string; + video: string; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + }; +} diff --git a/src/model/questionTypes/rating.ts b/src/model/questionTypes/rating.ts new file mode 100644 index 0000000..2626077 --- /dev/null +++ b/src/model/questionTypes/rating.ts @@ -0,0 +1,31 @@ +import type { + QuizQuestionBase, + QuestionHint, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionRating extends QuizQuestionBase { + type: "rating"; + content: { + id: string; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + steps: number; + ratingExpanded: boolean; + /** Форма иконки */ + form: string; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + /** Позитивное описание рейтинга */ + ratingPositiveDescription: string; + /** Негативное описание рейтинга */ + ratingNegativeDescription: string; + }; +} diff --git a/src/model/questionTypes/result.ts b/src/model/questionTypes/result.ts new file mode 100644 index 0000000..7636bd3 --- /dev/null +++ b/src/model/questionTypes/result.ts @@ -0,0 +1,22 @@ +import type { + QuizQuestionBase, + QuestionBranchingRule, + QuestionHint, +} from "./shared"; + +export interface QuizQuestionResult extends QuizQuestionBase { + type: "result"; + content: { + id: string; + back: string; + originalBack: string; + video: string; + innerName: string; + text: string; + price: [number] | [number, number]; + useImage: boolean; + rule: QuestionBranchingRule, + hint: QuestionHint; + autofill: boolean; + }; +} diff --git a/src/model/questionTypes/select.ts b/src/model/questionTypes/select.ts new file mode 100644 index 0000000..3145b80 --- /dev/null +++ b/src/model/questionTypes/select.ts @@ -0,0 +1,29 @@ +import type { + QuizQuestionBase, + QuestionVariant, + QuestionHint, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionSelect extends QuizQuestionBase { + type: "select"; + content: { + id: string; + /** Чекбокс "Можно несколько" */ + multi: boolean; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Поле "Текст в выпадающем списке" */ + default: string; + variants: QuestionVariant[]; + rule: QuestionBranchingRule; + hint: QuestionHint; + back: string; + originalBack: string; + autofill: boolean; + }; +} diff --git a/src/model/questionTypes/shared.ts b/src/model/questionTypes/shared.ts new file mode 100644 index 0000000..2d95ac3 --- /dev/null +++ b/src/model/questionTypes/shared.ts @@ -0,0 +1,120 @@ +import { QuestionType } from "@model/question/question"; +import type { QuizQuestionDate } from "./date"; +import type { QuizQuestionEmoji } from "./emoji"; +import type { QuizQuestionFile } from "./file"; +import type { QuizQuestionImages } from "./images"; +import type { QuizQuestionNumber } from "./number"; +import type { QuizQuestionPage } from "./page"; +import type { QuizQuestionRating } from "./rating"; +import type { QuizQuestionSelect } from "./select"; +import type { QuizQuestionText } from "./text"; +import type { QuizQuestionVariant } from "./variant"; +import type { QuizQuestionVarImg } from "./varimg"; +import type { QuizQuestionResult } from "./result"; +import { nanoid } from "nanoid"; + +export interface QuestionBranchingRuleMain { + next: string; + or: boolean; + rules: { + question: string; //id родителя (пока что) + answers: string[] + }[] +} +export interface QuestionBranchingRule { + + children: string[], + //список условий + main: QuestionBranchingRuleMain[]; + parentId: string | null | "root"; + default: string; +} + +export interface QuestionHint { + /** Текст подсказки */ + text: string; + /** URL видео подсказки */ + video: string; +} + +export type QuestionVariant = { + id: string; + /** Текст */ + answer: string; + /** Текст подсказки */ + hints: string; + /** Дополнительное поле для текста, emoji, ссылки на картинку */ + extendedText: string; + /** Оригинал изображения (до кропа) */ + originalImageUrl: string; +}; + +export interface QuizQuestionBase { + backendId: number; + /** Stable id, generated on client */ + id: string; + quizId: number; + title: string; + description: string; + page: number; + type?: QuestionType | null; + expanded: boolean; + openedModalSettings: boolean; + deleted: boolean; + deleteTimeoutId: number; + content: { + id: string; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + }; +} + +export interface UntypedQuizQuestion { + type: null; + id: string; + quizId: number; + title: string; + description: string; + expanded: boolean; + deleted: boolean; +} + +export type AnyTypedQuizQuestion = + | QuizQuestionVariant + | QuizQuestionImages + | QuizQuestionVarImg + | QuizQuestionEmoji + | QuizQuestionText + | QuizQuestionSelect + | QuizQuestionDate + | QuizQuestionNumber + | QuizQuestionFile + | QuizQuestionPage + | QuizQuestionRating + | QuizQuestionResult; + +type FilterQuestionsWithVariants = T extends { + content: { variants: QuestionVariant[]; }; +} ? T : never; + +export type QuizQuestionsWithVariants = FilterQuestionsWithVariants; + + +export const createBranchingRuleMain: (targetId:string, parentId:string) => QuestionBranchingRuleMain = (targetId, parentId) => ({ + next: targetId, + or: false, + rules: [{ + question: parentId, + answers: [] as string[], + }] +}) +export const createQuestionVariant: () => QuestionVariant = () => ({ + id: nanoid(), + answer: "", + extendedText: "", + hints: "", + originalImageUrl: "", +}); diff --git a/src/model/questionTypes/text.ts b/src/model/questionTypes/text.ts new file mode 100644 index 0000000..610bad5 --- /dev/null +++ b/src/model/questionTypes/text.ts @@ -0,0 +1,27 @@ +import type { + QuizQuestionBase, + QuestionHint, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionText extends QuizQuestionBase { + type: "text"; + content: { + id: string; + placeholder: string; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + /** Чекбокс "Автозаполнение адреса" */ + autofill: boolean; + answerType: "single" | "multi"; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + onlyNumbers: boolean; + }; +} diff --git a/src/model/questionTypes/variant.ts b/src/model/questionTypes/variant.ts new file mode 100644 index 0000000..54f457d --- /dev/null +++ b/src/model/questionTypes/variant.ts @@ -0,0 +1,32 @@ +import type { + QuizQuestionBase, + QuestionVariant, + QuestionHint, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionVariant extends QuizQuestionBase { + type: "variant"; + content: { + id: string; + /** Чекбокс "Длинный текстовый ответ" */ + largeCheck: boolean; + /** Чекбокс "Можно несколько" */ + multi: boolean; + /** Чекбокс "Вариант "свой ответ"" */ + own: boolean; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Варианты ответов */ + variants: QuestionVariant[]; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + }; +} diff --git a/src/model/questionTypes/varimg.ts b/src/model/questionTypes/varimg.ts new file mode 100644 index 0000000..6d274d3 --- /dev/null +++ b/src/model/questionTypes/varimg.ts @@ -0,0 +1,29 @@ +import type { + QuestionHint, + QuestionVariant, + QuizQuestionBase, + QuestionBranchingRule, +} from "./shared"; + +export interface QuizQuestionVarImg extends QuizQuestionBase { + type: "varimg"; + content: { + id: string; + /** Чекбокс "Вариант "свой ответ"" */ + own: boolean; + /** Чекбокс "Внутреннее название вопроса" */ + innerNameCheck: boolean; + /** Поле "Внутреннее название вопроса" */ + innerName: string; + /** Чекбокс "Необязательный вопрос" */ + required: boolean; + variants: QuestionVariant[]; + hint: QuestionHint; + rule: QuestionBranchingRule; + back: string; + originalBack: string; + autofill: boolean; + largeCheck: boolean; + replText: string; + }; +} diff --git a/src/model/quiz/copy.ts b/src/model/quiz/copy.ts new file mode 100644 index 0000000..804777c --- /dev/null +++ b/src/model/quiz/copy.ts @@ -0,0 +1,7 @@ +export interface CopyQuizRequest { + id: number; +} + +export interface CopyQuizResponse { + updated: number; +} diff --git a/src/model/quiz/create.ts b/src/model/quiz/create.ts new file mode 100644 index 0000000..c4271a7 --- /dev/null +++ b/src/model/quiz/create.ts @@ -0,0 +1,34 @@ +export interface CreateQuizRequest { + /** set true for save deviceId */ + fingerprinting: boolean; + /** set true for allow user to repeat quiz */ + repeatable: boolean; + /** set true for save statistic of incomplete quiz passing */ + note_prevented: boolean; + /** set true for mail notification for each quiz passing */ + mail_notifications: boolean; + /** set true for save statistics only for unique quiz passing */ + unique_answers: boolean; + /** name of quiz. max 280 length */ + name: string; + /** description of quiz */ + description: string; + /** config of quiz. serialized json for rules of quiz flow */ + config: string; + /** status of quiz. allow only '', 'draft', 'template', 'stop', 'start' */ + status: "draft" | "template" | "stop" | "start"; + /** limit is count of max quiz passing */ + limit: number; + /** last time when quiz is valid. timestamp in seconds */ + due_to: number; + /** seconds to pass quiz */ + time_of_passing: number; + /** true if it is allowed for pause quiz */ + pausable: boolean; + /** count of questions */ + question_cnt?: number; + /** set true if squiz realize group functionality */ + super: boolean; + /** group of new quiz */ + group_id: number; +} diff --git a/src/model/quiz/delete.ts b/src/model/quiz/delete.ts new file mode 100644 index 0000000..3cc0717 --- /dev/null +++ b/src/model/quiz/delete.ts @@ -0,0 +1,7 @@ +export interface DeleteQuizRequest { + id: number; +} + +export interface DeleteQuizResponse { + deactivated: number; +} diff --git a/src/model/quiz/edit.ts b/src/model/quiz/edit.ts new file mode 100644 index 0000000..a548a38 --- /dev/null +++ b/src/model/quiz/edit.ts @@ -0,0 +1,66 @@ +import { Quiz } from "./quiz"; + + +export interface EditQuizRequest { + /** id of question for update */ + id: number; + /** set true for storing fingerprints */ + fp: boolean; + /** set true for allow to repeat quiz after passing */ + rep: boolean; + /** set true for store unfinished passing */ + note_prevented: boolean; + /** set true if we should send passing result on every passing */ + mailing: boolean; + /** set true if we allow only one user quiz passing */ + uniq: boolean; + /** new name of the quiz */ + name?: string; + /** new descriptions of the quiz */ + desc?: string; + /** new config of the quiz */ + conf?: string; + /** new status. only draft,template,stop,start allowed */ + status?: string; + /** max amount of quiz passing */ + limit: number; + /** max time of quiz passing */ + due_to: number; + /** max time to pass quiz */ + time_of_passing: number; + /** allow to pause quiz to user */ + pausable: boolean; + /** count of questions */ + question_cnt?: number; + /** set true if squiz realize group functionality */ + super?: boolean; + /** group of new quiz */ + group_id?: number; +} + +export interface EditQuizResponse { + /** id of new version of question */ + updated: number; +} + +export function quizToEditQuizRequest(quiz: Quiz): EditQuizRequest { + return { + id: quiz.backendId, + fp: quiz.fingerprinting, + rep: quiz.repeatable, + note_prevented: quiz.note_prevented, + mailing: quiz.mail_notifications, + uniq: quiz.unique_answers, + name: quiz.name, + desc: quiz.description, + conf: JSON.stringify(quiz.config), + status: quiz.status, + limit: quiz.limit, + due_to: quiz.due_to, + time_of_passing: quiz.time_of_passing, + pausable: quiz.pausable, + question_cnt: quiz.question_cnt, + super: quiz.super, + group_id: quiz.group_id, + }; +} diff --git a/src/model/quiz/get.ts b/src/model/quiz/get.ts new file mode 100644 index 0000000..29988c7 --- /dev/null +++ b/src/model/quiz/get.ts @@ -0,0 +1,29 @@ +export interface GetQuizRequest { + quiz_id: string; + limit: number; + page: number; + need_config: boolean; +} + +export interface GetQuizResponse { + cnt: number; + settings: { + fp: boolean; + rep: boolean; + name: string; + cfg: string; + lim: number; + due: number; + delay: number; + pausable: boolean; + }; + items: { + id: number; + title: string; + desc: string; + typ: string; + req: boolean; + p: number; + c: string; + }[]; +} diff --git a/src/model/quiz/getList.ts b/src/model/quiz/getList.ts new file mode 100644 index 0000000..3abed8a --- /dev/null +++ b/src/model/quiz/getList.ts @@ -0,0 +1,29 @@ +import { RawQuiz } from "./quiz"; + +export interface GetQuizListRequest { + /** max items on page */ + limit?: number; + /** page number */ + offset?: number; + /** start time of time period. timestamp in seconds */ + from?: number; + /** end time of time period. timestamp in seconds */ + to?: number; + /** string for fulltext search in titles of quizes */ + search?: string; + /** allow only - draft, template, timeout, stop, start, offlimit */ + status?: "" | "draft" | "template" | "timeout" | "stop" | "start" | "offlimit"; + /** get deleted quizes */ + deleted?: boolean; + /** get archived quizes */ + archived?: boolean; + /** set true if squiz realize group functionality */ + super?: boolean; + /** group of new quiz */ + group_id?: number; +} + +export interface GetQuizListResponse { + count: number; + items: RawQuiz[]; +} diff --git a/src/model/quiz/quiz.ts b/src/model/quiz/quiz.ts new file mode 100644 index 0000000..0de02cd --- /dev/null +++ b/src/model/quiz/quiz.ts @@ -0,0 +1,133 @@ +import { QuizConfig, defaultQuizConfig } from "@model/quizSettings"; +import { nanoid } from "nanoid"; + + +export interface Quiz { + /** Stable id, generated on client */ + id: string; + /** Id of created quiz */ + backendId: number; + /** string id for customers */ + qid: string; + /** true if quiz deleted */ + deleted: boolean; + /** true if quiz archived */ + archived: boolean; + /** set true for save deviceId */ + fingerprinting: boolean; + /** set true for allow user to repeat quiz */ + repeatable: boolean; + /** set true for save statistic of incomplete quiz passing */ + note_prevented: boolean; + /** set true for mail notification for each quiz passing */ + mail_notifications: boolean; + /** set true for save statistics only for unique quiz passing */ + unique_answers: boolean; + /** name of quiz. max 280 length */ + name: string; + /** description of quiz */ + description: string; + /** quiz config*/ + config: QuizConfig; + /** status of quiz. allow only '', 'draft', 'template', 'stop', 'start' */ + status: string; + /** limit is count of max quiz passing */ + limit: number; + /** last time when quiz is valid. timestamp in seconds */ + due_to: number; + /** seconds to pass quiz */ + time_of_passing: number; + /** true if it is allowed for pause quiz */ + pausable: boolean; + /** version of quiz */ + version: number; + /** version comment to version of quiz */ + version_comment: string; + /** array of previous versions of quiz */ + parent_ids: number[]; + created_at: string; + updated_at: string; + /** count of questions */ + question_cnt: number; + /** count passings */ + passed_count: number; + /** average time of passing */ + average_time: number; + /** set true if squiz realize group functionality */ + super: boolean; + /** group of new quiz */ + group_id: number; +} + +/** Type that comes from server */ +export interface RawQuiz { + /** Id of created quiz */ + id: number; + /** string id for customers */ + qid: string; + /** true if quiz deleted */ + deleted: boolean; + /** true if quiz archived */ + archived: boolean; + /** set true for save deviceId */ + fingerprinting: boolean; + /** set true for allow user to repeat quiz */ + repeatable: boolean; + /** set true for save statistic of incomplete quiz passing */ + note_prevented: boolean; + /** set true for mail notification for each quiz passing */ + mail_notifications: boolean; + /** set true for save statistics only for unique quiz passing */ + unique_answers: boolean; + /** name of quiz. max 280 length */ + name: string; + /** description of quiz */ + description: string; + /** config of quiz. serialized json for rules of quiz flow */ + config: string; + /** status of quiz. allow only '', 'draft', 'template', 'stop', 'start' */ + status: string; + /** limit is count of max quiz passing */ + limit: number; + /** last time when quiz is valid. timestamp in seconds */ + due_to: number; + /** seconds to pass quiz */ + time_of_passing: number; + /** true if it is allowed for pause quiz */ + pausable: boolean; + /** version of quiz */ + version: number; + /** version comment to version of quiz */ + version_comment: string; + /** array of previous versions of quiz */ + parent_ids: number[]; + created_at: string; + updated_at: string; + /** count of questions */ + question_cnt: number; + /** count passings */ + passed_count: number; + /** average time of passing */ + average_time: number; + /** set true if squiz realize group functionality */ + super: boolean; + /** group of new quiz */ + group_id: number; +} + +export function rawQuizToQuiz(rawQuiz: RawQuiz): Quiz { + let config = defaultQuizConfig; + + try { + config = JSON.parse(rawQuiz.config); + } catch (error) { + console.warn("Cannot parse quiz config from string, using default config", error); + } + + return { + ...rawQuiz, + config, + backendId: rawQuiz.id, + id: nanoid(), + }; +} diff --git a/src/model/quizSettings.ts b/src/model/quizSettings.ts new file mode 100644 index 0000000..7be9e19 --- /dev/null +++ b/src/model/quizSettings.ts @@ -0,0 +1,110 @@ +import ChartPieIcon from "@icons/ChartPieIcon"; +import ContactBookIcon from "@icons/ContactBookIcon"; +import FlowArrowIcon from "@icons/FlowArrowIcon"; +import LayoutIcon from "@icons/LayoutIcon"; +import MegaphoneIcon from "@icons/MegaphoneIcon"; +import QuestionIcon from "@icons/QuestionIcon"; +import QuestionsMapIcon from "@icons/QuestionsMapIcon"; + + +export const quizSetupSteps = [ + { stepperText: "Настройка стартовой страницы", sidebarText: "Стартовая страница", sidebarIcon: LayoutIcon }, + { stepperText: "Задайте вопросы", sidebarText: "Вопросы", sidebarIcon: QuestionIcon }, + { stepperText: "Настройте авторезультаты", sidebarText: "Результаты", sidebarIcon: ChartPieIcon }, + // { stepperText: "Оценка графа карты вопросов", sidebarText: "Карта вопросов", sidebarIcon: QuestionsMapIcon }, + { stepperText: "Настройте форму контактов", sidebarText: "Форма контактов", sidebarIcon: ContactBookIcon }, + { stepperText: "Установите квиз", sidebarText: "Установка квиза", sidebarIcon: FlowArrowIcon }, + { stepperText: "Запустите рекламу", sidebarText: "Запуск рекламы", sidebarIcon: MegaphoneIcon }, +] as const; + +export const maxQuizSetupSteps = quizSetupSteps.length; + +export type QuizStartpageType = "standard" | "expanded" | "centered" | null; + +export type QuizStartpageAlignType = "left" | "right" | "center"; + +export type QuizType = "quiz" | "form" | null; + +export type QuizResultsType = true | null; + +export interface QuizConfig { + type: QuizType; + noStartPage: boolean; + startpageType: QuizStartpageType; + results: QuizResultsType; + haveRoot: string | null; + resultInfo: { + when: 'before' | 'after' | 'email', + share: true | false, + replay: true | false, + theme: string, + reply: string, + replname: string, + } + startpage: { + description: string; + button: string; + position: QuizStartpageAlignType; + favIcon: string | null; + logo: string | null; + originalLogo: string | null; + background: { + type: null | "image" | "video"; + desktop: string | null; + originalDesktop: string | null; + mobile: string | null; + originalMobile: string | null; + video: string | null; + cycle: boolean; + }; + }; + info: { + phonenumber: string; + clickable: boolean; + orgname: string; + site: string; + law?: string; + }; + meta: string; +} + +export const defaultQuizConfig: QuizConfig = { + type: null, + noStartPage: false, + startpageType: null, + results: null, + haveRoot: null, + resultInfo: { + when: 'after', + share: false, + replay: false, + theme: "", + reply: "", + replname: "", + }, + startpage: { + description: "", + button: "", + position: "left", + favIcon: null, + logo: null, + originalLogo: null, + background: { + type: null, + desktop: null, + originalDesktop: null, + mobile: null, + originalMobile: null, + video: null, + cycle: false, + }, + }, + info: { + phonenumber: "", + clickable: false, + orgname: "", + site: "", + law: "", + }, + meta: "", +}; diff --git a/src/mui.d.ts b/src/mui.d.ts new file mode 100644 index 0000000..446edc4 --- /dev/null +++ b/src/mui.d.ts @@ -0,0 +1,50 @@ +import "@material-ui/styles"; + +declare module "@mui/material/styles" { + interface Palette { + lightPurple: Palette["primary"], + darkPurple: Palette["primary"], + brightPurple: Palette["primary"], + fadePurple: Palette["primary"], + grey1: Palette["primary"], + grey2: Palette["primary"], + grey3: Palette["primary"], + grey4: Palette["primary"], + orange: Palette["primary"], + navbarbg: Palette["primary"], + } + interface PaletteOptions { + lightPurple?: PaletteOptions["primary"], + darkPurple?: PaletteOptions["primary"], + brightPurple?: PaletteOptions["primary"], + fadePurple?: PaletteOptions["primary"], + grey1?: PaletteOptions["primary"], + grey2?: PaletteOptions["primary"], + grey3?: PaletteOptions["primary"], + grey4?: PaletteOptions["primary"], + orange?: PaletteOptions["primary"], + navbarbg?: PaletteOptions["primary"], + } + interface TypographyVariants { + infographic: React.CSSProperties; + p1: React.CSSProperties; + } + interface TypographyVariantsOptions { + infographic?: React.CSSProperties; + p1?: React.CSSProperties; + } +} + +declare module "@mui/material/Typography" { + interface TypographyPropsVariantOverrides { + infographic: true; + p1: true; + } +} + +type DataAttributeKey = `data-${string}`; +declare module 'react' { + interface HTMLAttributes extends AriaAttributes, DOMAttributes { + [dataAttribute: DataAttributeKey]: unknown; + } +} diff --git a/src/pages/Questions/Select.tsx b/src/pages/Questions/Select.tsx new file mode 100644 index 0000000..5aa4037 --- /dev/null +++ b/src/pages/Questions/Select.tsx @@ -0,0 +1,138 @@ +import { useState, useEffect } from "react"; +import { + Select as MuiSelect, + MenuItem, + FormControl, + Typography, + useTheme, +} from "@mui/material"; + +import ArrowDown from "@icons/ArrowDownIcon"; + +import type { SelectChangeEvent, SxProps } from "@mui/material"; + +type SelectProps = { + items: string[]; + activeItemIndex?: number; + empty?: boolean; + onChange?: (item: string, num: number) => void; + sx?: SxProps; + placeholder?: string; +}; + +export const Select = ({ + items, + activeItemIndex = 0, + empty, + onChange, + sx, + placeholder = "", +}: SelectProps) => { + const [activeItem, setActiveItem] = useState( + empty ? -1 : activeItemIndex + ); + const theme = useTheme(); + + useEffect(() => { + setActiveItem(activeItemIndex); + }, [activeItemIndex]); + + const handleChange = (event: SelectChangeEvent) => { + const newItemIndex = Number(event.target.value); + + if (newItemIndex === activeItem) { + setActiveItem(-1); + onChange?.("", -1); + + return; + } + + setActiveItem(newItemIndex); + onChange?.(items[newItemIndex], newItemIndex); + }; + + return ( + + + value ? ( + items[Number(value)] + ) : ( + + {placeholder} + + ) + } + id="display-select" + variant="outlined" + value={activeItem === -1 ? "" : String(activeItem)} + onChange={handleChange} + sx={{ + width: "100%", + height: "48px", + borderRadius: "8px", + "& .MuiOutlinedInput-notchedOutline": { + border: `1px solid ${theme.palette.brightPurple.main} !important`, + height: "48px", + borderRadius: "10px", + }, + }} + MenuProps={{ + PaperProps: { + sx: { + mt: "8px", + p: "4px", + borderRadius: "8px", + border: "1px solid #EEE4FC", + boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)", + }, + }, + MenuListProps: { + sx: { + py: 0, + display: "flex", + flexDirection: "column", + gap: "8px", + "& .Mui-selected": { + backgroundColor: theme.palette.background.default, + color: theme.palette.brightPurple.main, + }, + }, + }, + }} + inputProps={{ + sx: { + color: theme.palette.brightPurple.main, + display: "flex", + alignItems: "center", + px: "9px", + gap: "20px", + }, + }} + IconComponent={(props) => } + > + {items.map((item, index) => ( + + {item} + + ))} + + + ); +}; diff --git a/src/pages/ViewPublicationPage/ContactForm.tsx b/src/pages/ViewPublicationPage/ContactForm.tsx new file mode 100644 index 0000000..d511f67 --- /dev/null +++ b/src/pages/ViewPublicationPage/ContactForm.tsx @@ -0,0 +1,47 @@ +import { Box, Typography, Button } from "@mui/material"; + +import { useCurrentQuiz } from "@root/quizes/hooks"; +import { useQuestionsStore } from "@root/questions/store"; + +import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; + +type ContactFormProps = { + currentQuestion: AnyTypedQuizQuestion; + showResultForm: boolean; + setShowContactForm: (show: boolean) => void; + setShowResultForm: (show: boolean) => void; +}; + +export const ContactForm = ({ + currentQuestion, + showResultForm, + setShowContactForm, + setShowResultForm, +}: ContactFormProps) => { + const quiz = useCurrentQuiz(); + const { questions } = useQuestionsStore(); + + const followNextForm = () => { + setShowContactForm(false); + setShowResultForm(true); + }; + + const resultQuestion = questions.find( + (question) => + question.type === "result" && + question.content.rule.parentId === currentQuestion.content.id + ); + + return ( + + Форма контактов + {!showResultForm && + resultQuestion && + quiz?.config.resultInfo.when === "after" && ( + + )} + + ); +}; diff --git a/src/pages/ViewPublicationPage/Footer.tsx b/src/pages/ViewPublicationPage/Footer.tsx new file mode 100644 index 0000000..a6708f3 --- /dev/null +++ b/src/pages/ViewPublicationPage/Footer.tsx @@ -0,0 +1,285 @@ +import { useState, useEffect } from "react"; +import { Box, Button, useTheme } from "@mui/material"; + +import { useQuizViewStore } from "@root/quizView"; +import { useCurrentQuiz } from "@root/quizes/hooks"; +import { useQuestionsStore } from "@root/questions/store"; + +import type { + AnyTypedQuizQuestion, + QuizQuestionBase, +} from "../../model/questionTypes/shared"; +import { getQuestionByContentId } from "@root/questions/actions"; +import { enqueueSnackbar } from "notistack"; + +type FooterProps = { + setCurrentQuestion: (step: AnyTypedQuizQuestion) => void; + question: AnyTypedQuizQuestion; + setShowContactForm: (show: boolean) => void; + setShowResultForm: (show: boolean) => void; +}; + +export const Footer = ({ + setCurrentQuestion, + question, + setShowContactForm, + setShowResultForm, +}: FooterProps) => { + const [disablePreviousButton, setDisablePreviousButton] = + useState(false); + const [disableNextButton, setDisableNextButton] = useState(false); + const quiz = useCurrentQuiz(); + const { answers } = useQuizViewStore(); + const questions = useQuestionsStore().questions as AnyTypedQuizQuestion[]; + const theme = useTheme(); + const linear = !questions.find( + ({ content }) => content.rule.parentId === "root" + ); + + useEffect(() => { + // Логика для аргумента disabled у кнопки "Назад" + if (linear) { + const questionIndex = questions.findIndex(({ id }) => id === question.id); + + const previousQuestion = questions[questionIndex - 1]; + + if (previousQuestion) { + setDisablePreviousButton(false); + } else { + setDisablePreviousButton(true); + } + } else { + if (question?.content.rule.parentId === "root") { + setDisablePreviousButton(true); + } else { + setDisablePreviousButton(false); + } + } + + // Логика для аргумента disabled у кнопки "Далее" + const answer = answers.find( + ({ questionId }) => questionId === question.content.id + ); + + if ("required" in question.content && question.content.required && answer) { + setDisableNextButton(false); + + return; + } + + if ( + "required" in question.content && + question.content.required && + !answer + ) { + setDisableNextButton(true); + + return; + } + + if (linear) { + return; + } + + const nextQuestionId = getNextQuestionId(); + if (nextQuestionId) { + setDisableNextButton(false); + } else { + const nextQuestion = getQuestionByContentId( + question.content.rule.default + ); + + if (nextQuestion?.type) { + setDisableNextButton(false); + } + } + }, [question, answers]); + + const showResult = () => { + const resultQuestion = questions.find( + ({ type, content }) => + type === "result" && content.rule.parentId === question.content.id + ); + + if (quiz?.config.resultInfo.when !== "after" && resultQuestion) { + setShowResultForm(true); + } else { + setShowContactForm(true); + } + }; + + const getNextQuestionId = () => { + if (answers.length) { + const answer = answers.find( + ({ questionId }) => questionId === question.content.id + ); + + let readyBeNextQuestion = ""; + + (question as QuizQuestionBase).content.rule.main.forEach( + ({ next, rules }) => { + let longerArray = Math.max( + rules[0].answers.length, + answer?.answer && Array.isArray(answer?.answer) + ? answer?.answer.length + : [answer?.answer].length + ); + + for ( + var i = 0; + i < longerArray; + i++ // Цикл по всем эле­мен­там бОльшего массива + ) { + if (Array.isArray(answer?.answer)) { + if ( + answer?.answer.find((item) => + String(item === rules[0].answers[i]) + ) + ) { + readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны + } + + return; + } + + if (String(rules[0].answers[i]) === answer?.answer) { + readyBeNextQuestion = next; // Ес­ли хоть один эле­мент от­ли­ча­ет­ся, мас­си­вы не рав­ны + } + } + } + ); + + return readyBeNextQuestion; + } + }; + + const followPreviousStep = () => { + if (linear) { + const questionIndex = questions.findIndex(({ id }) => id === question.id); + + const previousQuestion = questions[questionIndex - 1]; + + if (previousQuestion) { + setCurrentQuestion(previousQuestion); + } + + return; + } + + if (question?.content.rule.parentId !== "root") { + const parent = getQuestionByContentId(question?.content.rule.parentId); + if (parent?.type) { + setCurrentQuestion(parent); + } else { + enqueueSnackbar("не могу получить предыдущий вопрос"); + } + } else { + enqueueSnackbar("вы находитесь на первом вопросе"); + } + }; + + const followNextStep = () => { + if (linear) { + const questionIndex = questions.findIndex(({ id }) => id === question.id); + const nextQuestion = questions[questionIndex + 1]; + + if (nextQuestion && nextQuestion.type !== "result") { + setCurrentQuestion(nextQuestion); + } else { + showResult(); + } + + return; + } + + const nextQuestionId = getNextQuestionId(); + + if (nextQuestionId) { + const nextQuestion = getQuestionByContentId(nextQuestionId); + + if (nextQuestion?.type && nextQuestion.type !== "result") { + setCurrentQuestion(nextQuestion); + return; + } else { + enqueueSnackbar("не могу получить последующий вопрос"); + } + } else { + const nextQuestion = getQuestionByContentId( + question.content.rule.default + ); + if (nextQuestion?.type && nextQuestion.type !== "result") { + setCurrentQuestion(nextQuestion); + } else { + showResult(); + } + } + }; + + return ( + + + + {/* Шаг + + {stepNumber} */} + {/* */} + {/* Из + + {questions.length} + */} + + + + + + ); +}; diff --git a/src/pages/ViewPublicationPage/Question.tsx b/src/pages/ViewPublicationPage/Question.tsx new file mode 100644 index 0000000..5d05dbb --- /dev/null +++ b/src/pages/ViewPublicationPage/Question.tsx @@ -0,0 +1,108 @@ +import { useState, useEffect } from "react"; +import { Box } from "@mui/material"; + +import { useCurrentQuiz } from "@root/quizes/hooks"; +import { getQuestionByContentId } from "@root/questions/actions"; + +import { Variant } from "./questions/Variant"; +import { Images } from "./questions/Images"; +import { Varimg } from "./questions/Varimg"; +import { Emoji } from "./questions/Emoji"; +import { Text } from "./questions/Text"; +import { Select } from "./questions/Select"; +import { Date } from "./questions/Date"; +import { Number } from "./questions/Number"; +import { File } from "./questions/File"; +import { Page } from "./questions/Page"; +import { Rating } from "./questions/Rating"; +import { Footer } from "./Footer"; +import { ContactForm } from "./ContactForm"; +import { ResultForm } from "./ResultForm"; + +import type { QuestionType } from "../../model/question/question"; +import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; + +type QuestionProps = { + questions: AnyTypedQuizQuestion[]; +}; + +const QUESTIONS_MAP: any = { + variant: Variant, + images: Images, + varimg: Varimg, + emoji: Emoji, + text: Text, + select: Select, + date: Date, + number: Number, + file: File, + page: Page, + rating: Rating, +}; + +export const Question = ({ questions }: QuestionProps) => { + const quiz = useCurrentQuiz(); + const [currentQuestion, setCurrentQuestion] = + useState(); + const [showContactForm, setShowContactForm] = useState(false); + const [showResultForm, setShowResultForm] = useState(false); + + useEffect(() => { + const nextQuestion = getQuestionByContentId(quiz?.config.haveRoot || ""); + + if (nextQuestion?.type) { + setCurrentQuestion(nextQuestion); + + return; + } + + setCurrentQuestion(questions[0]); + }, []); + + if (!currentQuestion) return <>не смог отобразить вопрос; + + const QuestionComponent = + QUESTIONS_MAP[currentQuestion.type as Exclude]; + + return ( + + + {!showContactForm && !showResultForm && ( + + )} + {showContactForm && ( + + )} + {showResultForm && ( + + )} + + {!showContactForm && !showResultForm && ( +