This commit is contained in:
Pavel Torbeev 2023-09-08 19:03:31 +03:00
commit 07ea67ec81
222 changed files with 16671 additions and 0 deletions

113
.eslintrc.js Normal file
View File

@ -0,0 +1,113 @@
module.exports = {
env: {
node: true,
es6: true
},
parserOptions: {
ecmaVersion: 13,
sourceType: 'module',
jsx: true,
project: './tsconfig.json'
},
ignorePatterns: ['node_modules/*'],
extends: ['eslint:recommended', 'prettier'],
overrides: [
{
files: ['**/*.ts', '**/*.tsx'],
parser: '@typescript-eslint/parser',
settings: {
react: { version: 'detect' },
'import/resolver': {
typescript: {}
}
},
env: {
browser: true,
node: true,
es6: true
},
extends: [
'eslint:recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'prettier'
],
plugins: ['formatjs'],
rules: {
curly: ['warn', 'multi-line'],
'no-console': ['warn', { allow: ['warn', 'error', 'assert'] }],
'no-restricted-imports': [
'warn',
{
patterns: ['features/*/*']
}
],
'import/order': [
'warn',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
pathGroups: [
{
pattern: 'react',
group: 'external',
position: 'before'
},
{
pattern: 'react*',
group: 'external',
position: 'before'
},
{
pattern: './src/*',
group: 'internal',
position: 'after'
},
{
pattern: './*.[^(s?)css]',
group: 'index',
position: 'before'
},
{
pattern: './*.module.scss',
group: 'index',
position: 'after'
}
],
pathGroupsExcludedImportTypes: ['react']
}
],
'import/default': 'off',
'import/no-named-as-default-member': 'off',
'import/no-named-as-default': 'off',
'react/display-name': 'warn',
'react/prop-types': 'off',
'react/react-in-jsx-scope': 'off',
'jsx-a11y/anchor-is-valid': 'off',
'jsx-a11y/no-static-element-interactions': 'off',
'jsx-a11y/click-events-have-key-events': 'off',
'@typescript-eslint/no-unused-vars': ['warn', { varsIgnorePattern: '^_' }],
'@typescript-eslint/no-empty-interface': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-inferrable-types': 'off',
'formatjs/enforce-id': 'off',
'formatjs/no-id': 'error',
'formatjs/enforce-default-message': 'error',
'formatjs/enforce-description': 'error'
}
}
]
};

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# 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*

5
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

15
.idea/git_toolbox_prj.xml Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxProjectSettings">
<option name="commitMessageIssueKeyValidationOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
<option name="commitMessageValidationEnabledOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
</component>
</project>

12
.idea/hakaton.iml Normal file
View File

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

View File

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

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EslintConfiguration">
<option name="fix-on-save" value="true" />
</component>
</project>

8
.idea/modules.xml Normal file
View File

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

7
.idea/prettier.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
<option name="myRunOnSave" value="true" />
</component>
</project>

6
.idea/vcs.xml Normal file
View File

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

4
.idea/watcherTasks.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="SCSS" />
</project>

2
.prettierignore Normal file
View File

@ -0,0 +1,2 @@
/build
/coverage

12
.prettierrc.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
arrowParens: 'always',
bracketSameLine: true,
bracketSpacing: true,
endOfLine: 'auto',
printWidth: 120,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'none',
useTabs: false
};

46
README.md Normal file
View File

@ -0,0 +1,46 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `yarn start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.\
You will also see any lint errors in the console.
### `yarn test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `yarn build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `yarn eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).

74
package.json Normal file
View File

@ -0,0 +1,74 @@
{
"name": "hakaton",
"version": "0.1.0",
"private": true,
"dependencies": {
"@react-pdf/renderer": "^3.1.12",
"@seznam/compose-react-refs": "^1.0.6",
"@tanstack/react-query": "^4.33.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^6.6.0",
"@typescript-eslint/parser": "^6.6.0",
"axios": "^1.4.0",
"chart.js": "^4.4.0",
"clsx": "^2.0.0",
"date-fns": "^2.30.0",
"eslint": "^8.48.0",
"eslint-config-prettier": "^9.0.0",
"eslint-config-react-app": "^7.0.1",
"eslint-import-resolver-typescript": "^3.6.0",
"eslint-plugin-formatjs": "^4.10.4",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"focus-visible": "^5.2.0",
"prettier": "^3.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-hook-form": "^7.45.4",
"react-router-dom": "^6.15.0",
"react-scripts": "5.0.1",
"sanitize.css": "^13.0.0",
"sass": "^1.66.1",
"ts-key-enum": "^2.0.12",
"typescript": "^4.4.2",
"web-vitals": "^2.1.0",
"zustand": "^4.4.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/date-fns": "^2.6.0",
"@types/react-helmet": "^6.1.6"
}
}

View File

@ -0,0 +1,29 @@
{
"files": {
"main.css": "/static/css/main.5f2ac698.css",
"main.js": "/static/js/main.9948994c.js",
"static/js/787.d98de4bb.chunk.js": "/static/js/787.d98de4bb.chunk.js",
"static/media/background2.jpg": "/static/media/background2.ae2026eaf02a2a9549a0.jpg",
"static/media/Raleway-Bold.woff": "/static/media/Raleway-Bold.669ff3b37217225a0735.woff",
"static/media/Raleway-SemiBold.woff": "/static/media/Raleway-SemiBold.b4f6390736c5c84333f6.woff",
"static/media/Raleway-Regular.woff": "/static/media/Raleway-Regular.a582669062b72fd23826.woff",
"static/media/Raleway-Medium.woff": "/static/media/Raleway-Medium.b6832a08b63c3a92ca61.woff",
"static/media/Raleway-Bold.woff2": "/static/media/Raleway-Bold.f45100b58922b985f604.woff2",
"static/media/Raleway-SemiBold.woff2": "/static/media/Raleway-SemiBold.2a8d2efb19b3f056a7a5.woff2",
"static/media/Raleway-Regular.woff2": "/static/media/Raleway-Regular.8ef2f50ea57d9d13e2f7.woff2",
"static/media/Raleway-Medium.woff2": "/static/media/Raleway-Medium.7cc7d905f74a187d79f9.woff2",
"static/media/IBMPlexMono-Regular.woff": "/static/media/IBMPlexMono-Regular.67e636f5e0ef0cb3f90f.woff",
"static/media/IBMPlexMono-Regular.woff2": "/static/media/IBMPlexMono-Regular.255d8b9e69d1556eade4.woff2",
"static/media/upload.svg": "/static/media/upload.2378fb103ff39440964e02e2516d993e.svg",
"index.html": "/index.html",
"static/media/loader.svg": "/static/media/loader.852ca2d21732c012738d2807dc517f14.svg",
"static/media/right.svg": "/static/media/right.6dd63e7321721faf31750772f6ccc0c3.svg",
"main.5f2ac698.css.map": "/static/css/main.5f2ac698.css.map",
"main.9948994c.js.map": "/static/js/main.9948994c.js.map",
"787.d98de4bb.chunk.js.map": "/static/js/787.d98de4bb.chunk.js.map"
},
"entrypoints": [
"static/css/main.5f2ac698.css",
"static/js/main.9948994c.js"
]
}

BIN
pitch-deck/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

1
pitch-deck/index.html Normal file
View File

@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>React App</title><script defer="defer" src="/static/js/main.9948994c.js"></script><link href="/static/css/main.5f2ac698.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

BIN
pitch-deck/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
pitch-deck/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
pitch-deck/manifest.json Normal file
View File

@ -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": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
pitch-deck/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
"use strict";(self.webpackChunkhakaton=self.webpackChunkhakaton||[]).push([[787],{787:function(e,t,n){n.r(t),n.d(t,{getCLS:function(){return y},getFCP:function(){return h},getFID:function(){return F},getLCP:function(){return P},getTTFB:function(){return D}});var i,r,a,o,u=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:"v2-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},c=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){if("first-input"===e&&!("PerformanceEventTiming"in self))return;var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},f=function(e,t){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(e(i),t&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},s=function(e){addEventListener("pageshow",(function(t){t.persisted&&e(t)}),!0)},m=function(e,t,n){var i;return function(r){t.value>=0&&(r||n)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},v=-1,p=function(){return"hidden"===document.visibilityState?0:1/0},d=function(){f((function(e){var t=e.timeStamp;v=t}),!0)},l=function(){return v<0&&(v=p(),d(),s((function(){setTimeout((function(){v=p(),d()}),0)}))),{get firstHiddenTime(){return v}}},h=function(e,t){var n,i=l(),r=u("FCP"),a=function(e){"first-contentful-paint"===e.name&&(f&&f.disconnect(),e.startTime<i.firstHiddenTime&&(r.value=e.startTime,r.entries.push(e),n(!0)))},o=window.performance&&performance.getEntriesByName&&performance.getEntriesByName("first-contentful-paint")[0],f=o?null:c("paint",a);(o||f)&&(n=m(e,r,t),o&&a(o),s((function(i){r=u("FCP"),n=m(e,r,t),requestAnimationFrame((function(){requestAnimationFrame((function(){r.value=performance.now()-i.timeStamp,n(!0)}))}))})))},g=!1,T=-1,y=function(e,t){g||(h((function(e){T=e.value})),g=!0);var n,i=function(t){T>-1&&e(t)},r=u("CLS",0),a=0,o=[],v=function(e){if(!e.hadRecentInput){var t=o[0],i=o[o.length-1];a&&e.startTime-i.startTime<1e3&&e.startTime-t.startTime<5e3?(a+=e.value,o.push(e)):(a=e.value,o=[e]),a>r.value&&(r.value=a,r.entries=o,n())}},p=c("layout-shift",v);p&&(n=m(i,r,t),f((function(){p.takeRecords().map(v),n(!0)})),s((function(){a=0,T=-1,r=u("CLS",0),n=m(i,r,t)})))},E={passive:!0,capture:!0},w=new Date,L=function(e,t){i||(i=t,r=e,a=new Date,k(removeEventListener),S())},S=function(){if(r>=0&&r<a-w){var e={entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+r};o.forEach((function(t){t(e)})),o=[]}},b=function(e){if(e.cancelable){var t=(e.timeStamp>1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,t){var n=function(){L(e,t),r()},i=function(){r()},r=function(){removeEventListener("pointerup",n,E),removeEventListener("pointercancel",i,E)};addEventListener("pointerup",n,E),addEventListener("pointercancel",i,E)}(t,e):L(t,e)}},k=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(t){return e(t,b,E)}))},F=function(e,t){var n,a=l(),v=u("FID"),p=function(e){e.startTime<a.firstHiddenTime&&(v.value=e.processingStart-e.startTime,v.entries.push(e),n(!0))},d=c("first-input",p);n=m(e,v,t),d&&f((function(){d.takeRecords().map(p),d.disconnect()}),!0),d&&s((function(){var a;v=u("FID"),n=m(e,v,t),o=[],r=-1,i=null,k(addEventListener),a=p,o.push(a),S()}))},C={},P=function(e,t){var n,i=l(),r=u("LCP"),a=function(e){var t=e.startTime;t<i.firstHiddenTime&&(r.value=t,r.entries.push(e),n())},o=c("largest-contentful-paint",a);if(o){n=m(e,r,t);var v=function(){C[r.id]||(o.takeRecords().map(a),o.disconnect(),C[r.id]=!0,n(!0))};["keydown","click"].forEach((function(e){addEventListener(e,v,{once:!0,capture:!0})})),f(v,!0),s((function(i){r=u("LCP"),n=m(e,r,t),requestAnimationFrame((function(){requestAnimationFrame((function(){r.value=performance.now()-i.timeStamp,C[r.id]=!0,n(!0)}))}))}))}},D=function(e){var t,n=u("TTFB");t=function(){try{var t=performance.getEntriesByType("navigation")[0]||function(){var e=performance.timing,t={entryType:"navigation",startTime:0};for(var n in e)"navigationStart"!==n&&"toJSON"!==n&&(t[n]=Math.max(e[n]-e.navigationStart,0));return t}();if(n.value=n.delta=t.responseStart,n.value<0||n.value>performance.now())return;n.entries=[t],e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("load",(function(){return setTimeout(t,0)}))}}}]);
//# sourceMappingURL=787.d98de4bb.chunk.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,147 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/*!
* @kurkle/color v0.3.2
* https://github.com/kurkle/color#readme
* (c) 2023 Jukka Kurkela
* Released under the MIT License
*/
/*!
* Chart.js v4.4.0
* https://www.chartjs.org
* (c) 2023 Chart.js Contributors
* Released under the MIT License
*/
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
* @license MIT
*/
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* use-sync-external-store-shim/with-selector.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @preserve Knuth and Plass line breaking algorithm in JavaScript
*
* Licensed under the new BSD License.
* Copyright 2009-2010, Bram Stein
* All rights reserved.
*/
/**
* @remix-run/router v1.8.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/**
* React Router v6.15.0
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
/** @license React v0.17.0
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.23.0
* react-reconciler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C11.4474 3 10.9994 3.448 10.9994 4.00065C10.9994 4.55329 11.4474 5.00129 12 5.00129C15.8653 5.00129 18.9987 8.13472 18.9987 12C18.9987 15.8653 15.8653 18.9987 12 18.9987C8.13472 18.9987 5.00129 15.8653 5.00129 12C5.00129 11.4474 4.55329 10.9994 4.00065 10.9994C3.448 10.9994 3 11.4474 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 556 B

View File

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.834 9.99983H4.16731" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6673 14.1665L15.834 9.99983" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6673 5.83317L15.834 9.99983" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 494 B

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 14.25C3.41421 14.25 3.75 14.5858 3.75 15C3.75 16.4354 3.75159 17.4365 3.85315 18.1919C3.9518 18.9257 4.13225 19.3142 4.40901 19.591C4.68577 19.8678 5.07435 20.0482 5.80812 20.1469C6.56347 20.2484 7.56459 20.25 9 20.25H15C16.4354 20.25 17.4365 20.2484 18.1919 20.1469C18.9257 20.0482 19.3142 19.8678 19.591 19.591C19.8678 19.3142 20.0482 18.9257 20.1469 18.1919C20.2484 17.4365 20.25 16.4354 20.25 15C20.25 14.5858 20.5858 14.25 21 14.25C21.4142 14.25 21.75 14.5858 21.75 15V15.0549C21.75 16.4225 21.75 17.5248 21.6335 18.3918C21.5125 19.2919 21.2536 20.0497 20.6517 20.6516C20.0497 21.2536 19.2919 21.5125 18.3918 21.6335C17.5248 21.75 16.4225 21.75 15.0549 21.75H8.94513C7.57754 21.75 6.47522 21.75 5.60825 21.6335C4.70814 21.5125 3.95027 21.2536 3.34835 20.6517C2.74643 20.0497 2.48754 19.2919 2.36652 18.3918C2.24996 17.5248 2.24998 16.4225 2.25 15.0549C2.25 15.0366 2.25 15.0183 2.25 15C2.25 14.5858 2.58579 14.25 3 14.25Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2.25C12.2106 2.25 12.4114 2.33852 12.5535 2.49392L16.5535 6.86892C16.833 7.17462 16.8118 7.64902 16.5061 7.92852C16.2004 8.20802 15.726 8.18678 15.4465 7.88108L12.75 4.9318V16C12.75 16.4142 12.4142 16.75 12 16.75C11.5858 16.75 11.25 16.4142 11.25 16V4.9318L8.55353 7.88108C8.27403 8.18678 7.79963 8.20802 7.49393 7.92852C7.18823 7.64902 7.16698 7.17462 7.44648 6.86892L11.4465 2.49392C11.5886 2.33852 11.7894 2.25 12 2.25Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

43
public/index.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View File

@ -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": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

35
src/api/deck/convert.ts Normal file
View File

@ -0,0 +1,35 @@
import {axios} from '../../lib/axios';
import {CONVERT_API_URL} from './urlKeys';
import {MutationConfig} from '../../lib/react-query';
import {useMutation} from '@tanstack/react-query';
export type ConvertDTO = {
pdf: File;
};
export type ConvertResponse = {
pdf: string;
pptx: string;
};
export const convert = (data: ConvertDTO): Promise<ConvertResponse> => {
const inputData = new FormData();
inputData.append('pdf', data.pdf);
return axios.post(CONVERT_API_URL, inputData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
};
type UseConvertOptions = {
config?: MutationConfig<typeof convert>;
};
export const useConvert = ({ config }: UseConvertOptions = {}) => {
return useMutation({
...config,
mutationFn: convert
});
};

View File

@ -0,0 +1,65 @@
import { useMutation } from '@tanstack/react-query';
import { MutationConfig, queryClient } from '../../lib/react-query';
import { axios } from '../../lib/axios';
import { Answer, PitchDeck } from './types';
import { DECKS_API_URL, QUESTION_API_URL, QUESTION_PARAM_DECK_ID, QUESTION_PARAM_QUESTION_ID } from './urlKeys';
import { QUERY_KEY_ANSWER } from './queryKeys';
export type CreateAnswerDTO = {
deckId: number;
questionId: number;
answer: any;
isFile?: boolean;
file?: File;
};
export type CreateAnswerResponse = Answer;
export const createAnswer = (data: CreateAnswerDTO): Promise<CreateAnswerResponse> => {
const path =
QUESTION_API_URL.replace(`:${QUESTION_PARAM_DECK_ID}`, String(data.deckId)).replace(
`:${QUESTION_PARAM_QUESTION_ID}`,
String(data.questionId)
) + '/';
let inputData: any;
if (data.isFile) {
inputData = new FormData();
if (data.answer) {
inputData.append('answer', `${data.answer}`);
}
for (const key in data) {
if (key !== 'answer' && key !== 'isFile' && key !== 'deckId' && key !== 'questionId') {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
inputData.append(key, data[key]);
}
}
} else {
inputData = {
answer: data.answer
};
}
return axios.post(path, inputData, {
headers: data.isFile
? {
'Content-Type': 'multipart/form-data'
}
: {}
});
};
type UseCreateAnswerOptions = {
config?: MutationConfig<typeof createAnswer>;
};
export const useCreateAnswer = ({ config }: UseCreateAnswerOptions = {}) => {
return useMutation({
onMutate: async () => {
await queryClient.cancelQueries([QUERY_KEY_ANSWER]);
},
...config,
mutationFn: createAnswer
});
};

View File

@ -0,0 +1,29 @@
import {PitchDeck} from './types';
import {DECKS_API_URL} from './urlKeys';
import {MutationConfig, queryClient} from '../../lib/react-query';
import {useMutation} from '@tanstack/react-query';
import {QUERY_KEY_DECKS} from './queryKeys';
import { axios } from '../../lib/axios';
export type CreateDeckDTO = Pick<PitchDeck, 'description'>;
export type CreateDeckResponse = PitchDeck;
export const createDeck = (data: CreateDeckDTO): Promise<CreateDeckResponse> => {
return axios.post(DECKS_API_URL, data);
};
type UseCreateDeckOptions = {
config?: MutationConfig<typeof createDeck>;
};
export const useCreateDeck = ({ config }: UseCreateDeckOptions = {}) => {
return useMutation({
onMutate: async () => {
await queryClient.cancelQueries([QUERY_KEY_DECKS]);
},
...config,
mutationFn: createDeck
});
};

50
src/api/deck/getDeck.ts Normal file
View File

@ -0,0 +1,50 @@
import {Question} from './types';
import {axios} from '../../lib/axios';
import {
DECK_API_URL,
QUESTION_API_URL,
QUESTION_PARAM_DECK_ID,
QUESTION_PARAM_QUESTION_ID,
} from './urlKeys';
import {ExtractFnReturnType, QueryConfig} from '../../lib/react-query';
import {useQuery} from '@tanstack/react-query';
import {QUERY_KEY_DECKS, QUERY_KEY_QUESTION} from './queryKeys';
export type GetDeckResponse = {
deck: {
name: string;
description: string;
},
slides: {
slide: number;
data: {
slug: string;
answer: any;
photos?: string[]
}[];
}[];
};
export const getDeck = ({ deckId }: { deckId: number }): Promise<GetDeckResponse> => {
return axios.get(
DECK_API_URL
.replace(`:${QUESTION_PARAM_DECK_ID}`, String(deckId))
);
};
type QueryFnType = typeof getDeck;
type UseDeckOptions = {
deckId: number;
config?: QueryConfig<QueryFnType>;
};
export const useDeck = ({ deckId, config }: UseDeckOptions) => {
return useQuery<ExtractFnReturnType<QueryFnType>>({
...config,
queryKey: [QUERY_KEY_DECKS, deckId],
queryFn: async () => {
return await getDeck({ deckId });
},
});
};

View File

@ -0,0 +1,31 @@
import {Question} from './types';
import {axios} from '../../lib/axios';
import {FIRST_QUESTION_API_URL, FIRST_QUESTION_PARAM} from './urlKeys';
import {ExtractFnReturnType, QueryConfig} from '../../lib/react-query';
import {useQuery} from '@tanstack/react-query';
import {QUERY_KEY_FIRST_QUESTION} from './queryKeys';
export type GetFirstQuestionResponse = Question;
export const getFirstQuestion = ({ deckId }: { deckId: number; }): Promise<GetFirstQuestionResponse> => {
return axios.get(FIRST_QUESTION_API_URL.replace(`:${FIRST_QUESTION_PARAM}`, String(deckId)));
};
type QueryFnType = typeof getFirstQuestion;
type UseFirstQuestionOptions = {
deckId: number;
config?: QueryConfig<QueryFnType>;
};
export const useFirstQuestion = ({ deckId, config }: UseFirstQuestionOptions) => {
return useQuery<ExtractFnReturnType<QueryFnType>>({
...config,
queryKey: [QUERY_KEY_FIRST_QUESTION, deckId],
queryFn: async () => {
const process = await getFirstQuestion({ deckId });
return process;
},
});
};

View File

@ -0,0 +1,38 @@
import {Question} from './types';
import {axios} from '../../lib/axios';
import {
QUESTION_API_URL,
QUESTION_PARAM_DECK_ID,
QUESTION_PARAM_QUESTION_ID,
} from './urlKeys';
import {ExtractFnReturnType, QueryConfig} from '../../lib/react-query';
import {useQuery} from '@tanstack/react-query';
import {QUERY_KEY_QUESTION} from './queryKeys';
export type GetQuestionResponse = Question;
export const getQuestion = ({ deckId, questionId }: { deckId: number; questionId: number; }): Promise<GetQuestionResponse> => {
return axios.get(
QUESTION_API_URL
.replace(`:${QUESTION_PARAM_DECK_ID}`, String(deckId))
.replace(`:${QUESTION_PARAM_QUESTION_ID}`, String(questionId))
);
};
type QueryFnType = typeof getQuestion;
type UseQuestionOptions = {
deckId: number;
questionId: number;
config?: QueryConfig<QueryFnType>;
};
export const useQuestion = ({ deckId, questionId, config }: UseQuestionOptions) => {
return useQuery<ExtractFnReturnType<QueryFnType>>({
...config,
queryKey: [QUERY_KEY_QUESTION, deckId, questionId],
queryFn: async () => {
return await getQuestion({ deckId, questionId });
},
});
};

6
src/api/deck/index.ts Normal file
View File

@ -0,0 +1,6 @@
export * from './types';
export * from './urlKeys';
export * from './queryKeys';
export * from './createDeck';
export * from './getFirstQuestion';

View File

@ -0,0 +1,4 @@
export const QUERY_KEY_DECKS = 'decks';
export const QUERY_KEY_FIRST_QUESTION = 'firstQuestion';
export const QUERY_KEY_QUESTION = 'question';
export const QUERY_KEY_ANSWER = 'answer';

45
src/api/deck/types.ts Normal file
View File

@ -0,0 +1,45 @@
export enum EntityType {
text = 'text', // +
number = 'number', // +
range = 'range', // +
multiple_range = 'multiple_range', // +
select = 'select', // +
link = 'link', // + добавить валидацию
date = 'date', // +
// photo = 'photo',
multiple_photo = 'multiple_photo',
photo_description = 'photo_description',
multiple_link_description = 'multiple_link_description', //
multiple_photo_description = 'multiple_photo_description',
// multiple_links = 'multiple_links', // не пригодилось
multiple_date_description = 'multiple_date_description', //
text_array = 'text_array', // используется только в подсказке
cards = 'cards', // используется только в подсказке
}
export type PitchDeck = {
id: number;
name: string;
description?: string;
questions?: any[];
};
export type Hint = {
type: EntityType;
value: any;
}
export type Question = {
id: number;
text: string;
type: EntityType;
hint: Hint | false;
next_id: number;
params: { [key: string]: any } | null;
};
export type Answer = {
answer: any;
deck: number;
question: number;
}

13
src/api/deck/urlKeys.ts Normal file
View File

@ -0,0 +1,13 @@
export const DECKS_API_URL = '/decks/';
export const FIRST_QUESTION_PARAM = 'deckId';
export const FIRST_QUESTION_API_URL = `/decks/question/:${FIRST_QUESTION_PARAM}`;
export const QUESTION_PARAM_DECK_ID = 'deckId';
export const QUESTION_PARAM_QUESTION_ID = 'questionId';
export const QUESTION_API_URL = `/decks/question/:${FIRST_QUESTION_PARAM}/:${QUESTION_PARAM_QUESTION_ID}`;
export const DECK_PARAM = 'deckId';
export const DECK_API_URL = `/decks/question/:${FIRST_QUESTION_PARAM}/presentation`;
export const CONVERT_API_URL = '/decks/pdf-to-pptx';

15
src/app/App.tsx Normal file
View File

@ -0,0 +1,15 @@
import React from 'react';
import { Helmet } from 'react-helmet';
import {AppRoutes} from './routes';
export function App() {
return (
<>
<Helmet title={"Pitch Deck"}>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
</Helmet>
<AppRoutes />
</>
);
}

13
src/app/AppProvider.tsx Normal file
View File

@ -0,0 +1,13 @@
import {FC, PropsWithChildren} from 'react';
import {BrowserRouter} from 'react-router-dom';
import {ReactQueryProvider} from './providers';
export const AppProvider: FC<PropsWithChildren> = ({ children }) => {
return (
<BrowserRouter>
<ReactQueryProvider>
{children}
</ReactQueryProvider>
</BrowserRouter>
)
}

2
src/app/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './App';
export * from './AppProvider';

View File

@ -0,0 +1 @@
export * from './react-query';

View File

@ -0,0 +1,11 @@
import {ReactFCC} from '../../utils/ReactFCC';
import {QueryClientProvider} from '@tanstack/react-query';
import {queryClient} from '../../lib/react-query';
export const ReactQueryProvider: ReactFCC = ({ children }) => {
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
)
}

View File

@ -0,0 +1,19 @@
import {Route, Routes} from 'react-router-dom';
import {ChatPage} from '../../pages/chat';
import {CHAT_PAGE_ROUTE, HOME_PAGE_ROUTE} from './routes';
import {HomePage} from '../../pages/home';
import {DefaultLayout} from '../../pages/_layouts/DefaultLayout';
export const AppRoutes = () => {
return (
<Routes>
<Route element={<DefaultLayout />}>
<Route path={CHAT_PAGE_ROUTE} element={<ChatPage />} />
<Route path={HOME_PAGE_ROUTE} element={<HomePage />} />
</Route>
</Routes>
);
};

2
src/app/routes/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './AppRoutes';
export * from './routes';

2
src/app/routes/routes.ts Normal file
View File

@ -0,0 +1,2 @@
export const CHAT_PAGE_ROUTE = `/chat`;
export const HOME_PAGE_ROUTE = `/`;

View File

@ -0,0 +1,160 @@
$font-family-default: system-ui, /* macOS 10.11-10.12 */ -apple-system, /* Windows 6+ */ 'Segoe UI',
/* Android 4+ */ 'Roboto', /* Ubuntu 10.10+ */ 'Ubuntu', /* Gnome 3+ */ 'Cantarell', /* KDE Plasma 5+ */ 'Noto Sans',
/* fallback */ sans-serif, /* macOS emoji */ 'Apple Color Emoji', /* Windows emoji */ 'Segoe UI Emoji',
/* Windows emoji */ 'Segoe UI Symbol', /* Linux emoji */ 'Noto Color Emoji';
// Моноширинный набор шрифтов.
$font-family-mono: 'Menlo', 'Consolas', 'Roboto Mono', 'Ubuntu Monospace', 'Noto Mono', 'Oxygen Mono', 'Liberation Mono',
monospace, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
$font-family-raleway: 'Raleway';
$font-family-inter: 'Inter';
$font-family-ibm-plex-mono: 'IBM Plex Mono';
$font-size-10: 10px;
$font-size-12: 12px;
$font-size-14: 14px;
$font-size-16: 16px;
$font-size-18: 18px;
$font-size-24: 24px;
$font-size-32: 32px;
$font-size-56: 56px;
$font-weight-400: 400;
$font-weight-500: 500;
$font-weight-600: 600;
$font-weight-700: 700;
$font-weight-regular: 400;
$font-weight-medium: 500;
$font-weight-semibold: 600;
$font-weight-bold: 700;
$line-height-16: 16px;
$line-height-18: 18px;
$line-height-20: 20px;
$line-height-24: 24px;
$line-height-32: 32px;
$line-height-40: 40px;
$line-height-67: 67px;
@mixin text-header-h1 {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-56;
font-weight: $font-weight-700;
line-height: $line-height-67;
}
@mixin text-header-h2 {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-32;
font-weight: $font-weight-700;
line-height: $line-height-40;
}
@mixin text-header-h3 {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-24;
font-weight: $font-weight-700;
line-height: $line-height-32;
}
@mixin text-header-h4 {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-18;
font-weight: $font-weight-700;
line-height: $line-height-24;
}
@mixin text-body-l-medium {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-18;
font-weight: $font-weight-600;
line-height: $line-height-24;
}
@mixin text-body-l-regular {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-18;
font-weight: $font-weight-400;
line-height: $line-height-24;
}
@mixin text-body-m-medium {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-16;
font-weight: $font-weight-600;
line-height: $line-height-20;
}
@mixin text-body-m-regular {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-16;
font-weight: $font-weight-400;
line-height: $line-height-20;
}
@mixin text-body-s-medium {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-14;
font-weight: $font-weight-600;
line-height: $line-height-18;
}
@mixin text-body-s-regular {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-12;
font-weight: $font-weight-400;
line-height: $line-height-18;
}
@mixin text-caption-m-medium {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-14;
font-weight: $font-weight-500;
line-height: $line-height-20;
}
@mixin text-caption-m-regular {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-14;
font-weight: $font-weight-400;
line-height: $line-height-20;
}
@mixin text-caption-s-medium {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-10;
font-weight: $font-weight-500;
line-height: $line-height-16;
}
@mixin text-caption-s-regular {
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-10;
font-weight: $font-weight-400;
line-height: $line-height-16;
}
@mixin text-caption-all-caps {
letter-spacing: 0.04em;
text-transform: uppercase;
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-12;
font-weight: $font-weight-600;
line-height: $line-height-20;
}
@mixin text-programming-code-medium {
font-family: $font-family-ibm-plex-mono, $font-family-mono;
font-size: $font-size-12;
font-weight: $font-weight-400;
line-height: $line-height-20;
}
@mixin text-programming-code-regular {
font-family: $font-family-ibm-plex-mono, $font-family-mono;
font-size: $font-size-12;
font-weight: $font-weight-400;
line-height: $line-height-16;
}

View File

@ -0,0 +1,137 @@
$breakpoint-mobile-small: 320px;
$breakpoint-mobile-large: 375px;
$breakpoint-tablet-small: 768px;
$breakpoint-tablet-large: 1024px;
$breakpoint-desktop-small: 1280px;
$breakpoint-desktop-medium: 1440px;
$breakpoint-desktop-large: 1920px;
@mixin media-up($breakpoint) {
@if $breakpoint == mobile-small {
@media screen and (min-width: $breakpoint-mobile-small) {
@content;
}
} @else if $breakpoint == mobile-large {
@media screen and (min-width: $breakpoint-mobile-large) {
@content;
}
} @else if $breakpoint == tablet-small {
@media screen and (min-width: $breakpoint-tablet-small) {
@content;
}
} @else if $breakpoint == tablet-large {
@media screen and (min-width: $breakpoint-tablet-large) {
@content;
}
} @else if $breakpoint == desktop-small {
@media screen and (min-width: $breakpoint-desktop-small) {
@content;
}
} @else if $breakpoint == desktop-medium {
@media screen and (min-width: $breakpoint-desktop-medium) {
@content;
}
} @else if $breakpoint == desktop-large {
@media screen and (min-width: $breakpoint-desktop-large) {
@content;
}
}
}
@mixin media-down($breakpoint) {
@if $breakpoint == mobile-small {
@media screen and (max-width: $breakpoint-mobile-small - 1) {
@content;
}
} @else if $breakpoint == mobile-large {
@media screen and (max-width: $breakpoint-mobile-large - 1) {
@content;
}
} @else if $breakpoint == tablet-small {
@media screen and (max-width: $breakpoint-tablet-small - 1) {
@content;
}
} @else if $breakpoint == tablet-large {
@media screen and (max-width: $breakpoint-tablet-large - 1) {
@content;
}
} @else if $breakpoint == desktop-small {
@media screen and (max-width: $breakpoint-desktop-small - 1) {
@content;
}
} @else if $breakpoint == desktop-medium {
@media screen and (max-width: $breakpoint-desktop-medium - 1) {
@content;
}
} @else if $breakpoint == desktop-large {
@media screen and (max-width: $breakpoint-desktop-large - 1) {
@content;
}
}
}
@mixin grid-for($breakpoint) {
display: grid;
@if $breakpoint == mobile-small {
display: grid;
grid-template-columns: repeat(4, 1fr);
padding-right: 16px;
padding-left: 16px;
column-gap: 16px;
} @else if $breakpoint == mobile-large {
grid-template-columns: repeat(4, 1fr);
padding-right: 16px;
padding-left: 16px;
column-gap: 16px;
} @else if $breakpoint == tablet-small {
grid-template-columns: repeat(8, 1fr);
padding-right: 72px;
padding-left: 72px;
column-gap: 16px;
} @else if $breakpoint == tablet-large {
grid-template-columns: repeat(12, 1fr);
padding-right: 40px;
padding-left: 40px;
column-gap: 16px;
} @else if $breakpoint == desktop-small {
grid-template-columns: repeat(12, 1fr);
padding-right: 76px;
padding-left: 76px;
column-gap: 24px;
} @else if $breakpoint == desktop-medium {
grid-template-columns: repeat(12, 1fr);
padding-right: 112px;
padding-left: 112px;
column-gap: 32px;
} @else if $breakpoint == desktop-large {
grid-template-columns: repeat(16, 1fr);
padding-right: 144px;
padding-left: 144px;
column-gap: 32px;
}
}
@mixin mobile-down {
@include media-down(tablet-small) {
@content;
}
}
@mixin mobile-up {
@include media-up(tablet-small) {
@content;
}
}
@mixin tablet-down {
@include media-down(desktop-small) {
@content;
}
}
@mixin tablet-up {
@include media-up(desktop-small) {
@content;
}
}

131
src/app/styles/colors.scss Normal file
View File

@ -0,0 +1,131 @@
@use 'sass:color' as color;
// бело-коричневый
//$color-text: #1b1918;
//$color-background: #fafafa;
//$color-primary: #7e716d;
//$color-secondary: #e7e5e4;
//$color-accent: #887b77;
// синий
$color-text: #010819;
$color-background: #e6edfe;
$color-primary: #0647e0;
$color-secondary: #cddcfe;
$color-accent: #064ff9;
// сине-пастельный
//$color-text: #130e07;
//$color-background: #fbf8f4;
//$color-primary: #223a5e;
//$color-secondary: #ebdcc7;
//$color-accent: #3d68a9;
// синий монохром
//$color-text: #01062d;
//$color-background: #ebedfe;
//$color-primary: #3e51f9;
//$color-secondary: #b5bcfd;
//$color-accent: #071cdf;
$color-background-primary: $color-background;
$color-background-secondary: $color-secondary;
$color-background-dark-100: darken($color-background, 1%);
$color-background-dark-200: darken($color-background, 2%);
$color-background-dark-300: darken($color-background, 3%);
$color-background-dark-400: darken($color-background, 4%);
$color-background-accent: $color-accent;
$color-text-primary: $color-text;
$color-text-secondary: color.adjust($color-background, $blackness: 100%);
$color-text-tertiary: color.adjust($color-background, $blackness: 70%);
$color-text-quaternary: $color-background;
$color-text-brand: $color-primary;
$color-text-disabled: color.adjust($color-text, $lightness: 40%);
$color-brand-primary: $color-primary;
$color-brand-hover: color.adjust($color-primary, $blackness: 2%);
$color-brand-disabled: color.adjust($color-primary, $lightness: 2%);
$color-border: color.invert($color-background);
//@debug $color-border;
//
/* --- surface --- */
$color-surface-primary: #141414;
/* --- on-surface --- */
$color-on-surface-dark-100: #1d1d1d;
$color-on-surface-dark-200: #252525;
$color-on-surface-dark-300: #2d2d2d;
$color-on-surface-dark-400: #3e3e3e;
$color-on-surface-light-400: #5e6166;
$color-on-surface-light-300: #868a92;
$color-on-surface-light-200: #c5c6ca;
$color-on-surface-light-100: #fafafa;
$color-on-surface-quaternary: #2b2b2b;
/* --- text --- */
//$color-text-primary: #fafafa;
//$color-text-secondary: #c5c6ca;
//$color-text-tertiary: #868a92;
//$color-text-quaternary: #5e6166;
//$color-text-dark: #2b2b2b;
//$color-text-brand: #8e85e5;
/* --- brand --- */
//$color-brand-primary: #695fcf;
//$color-brand-hover: #9288f8;
//$color-brand-disabled: #49428e;
/* --- system --- */
$color-system-link: #7f9ef3;
$color-system-success: #10b981;
$color-system-warning: #f59e0b;
$color-system-error: #f43f5e;
$color-system-link-weak: #142144;
$color-system-success-weak: #042c1f;
$color-system-warning-weak: #3d2907;
$color-system-error-weak: #2d1015;
$color-system-link-hover: #a8c1f8;
/* --- accent --- */
$color-accent-blue-100: #7f9ef3;
$color-accent-blue-200: #2b4acb;
$color-accent-blue-300: #203175;
$color-accent-blue-400: #191e40;
$color-accent-pink-100: #f37fb7;
$color-accent-pink-200: #cb2b83;
$color-accent-pink-300: #75204f;
$color-accent-pink-400: #40162f;
$color-accent-purple-100: #ab7ae0;
$color-accent-purple-200: #642ab5;
$color-accent-purple-300: #3e2069;
$color-accent-purple-400: #21183b;
$color-accent-green-100: #8fd460;
$color-accent-green-200: #49aa19;
$color-accent-green-300: #306317;
$color-accent-green-400: #1d3712;
$color-accent-cyan-100: #58d1c9;
$color-accent-cyan-200: #13a8a8;
$color-accent-cyan-300: #146262;
$color-accent-cyan-400: #163b2e;
$color-accent-orange-100: #f3b765;
$color-accent-orange-200: #d87a16;
$color-accent-orange-300: #7c4a15;
$color-accent-orange-400: #442a11;
$color-accent-yellow-100: #f3ea62;
$color-accent-yellow-200: #d8bd14;
$color-accent-yellow-300: #7c6e14;
$color-accent-yellow-400: #443611;
$color-accent-red-100: #f37370;
$color-accent-red-200: #d32029;
$color-accent-red-300: #791a1f;
$color-accent-red-400: #421716;
//
//$text: #131513;
//$background: #f4f5f4;
//$primary: #5d555e;
//$secondary: #d8d4d6;
//$accent: #706b76;

81
src/app/styles/fonts.scss Normal file
View File

@ -0,0 +1,81 @@
@import './baseText';
$fonts-path: '../../assets/fonts' !default;
@font-face {
font-family: $font-family-raleway;
src: url('#{$fonts-path}/Raleway/Raleway-Regular.woff2') format('woff2'),
url('#{$fonts-path}/Raleway/Raleway-Regular.woff') format('woff');
font-weight: $font-weight-400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: $font-family-raleway;
src: url('#{$fonts-path}/Raleway/Raleway-Medium.woff2') format('woff2'),
url('#{$fonts-path}/Raleway/Raleway-Medium.woff') format('woff');
font-weight: $font-weight-500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: $font-family-raleway;
src: url('#{$fonts-path}/Raleway/Raleway-SemiBold.woff2') format('woff2'),
url('#{$fonts-path}/Raleway/Raleway-SemiBold.woff') format('woff');
font-weight: $font-weight-600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: $font-family-raleway;
src: url('#{$fonts-path}/Raleway/Raleway-Bold.woff2') format('woff2'),
url('#{$fonts-path}/Raleway/Raleway-Bold.woff') format('woff');
font-weight: $font-weight-700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: $font-family-inter;
src: url('#{$fonts-path}/Inter/Inter-Regular.ttf') format('truetype');
font-weight: $font-weight-regular;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: $font-family-inter;
src: url('#{$fonts-path}/Inter/Inter-Medium.ttf') format('truetype');
font-weight: $font-weight-medium;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: $font-family-inter;
src: url('#{$fonts-path}/Inter/Inter-SemiBold.ttf') format('truetype');
font-weight: $font-weight-semibold;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: $font-family-inter;
src: url('#{$fonts-path}/Inter/Inter-Bold.ttf') format('truetype');
font-weight: $font-weight-bold;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: $font-family-ibm-plex-mono;
src: url('#{$fonts-path}/IBM_Plex_Mono/IBMPlexMono-Regular.woff2') format('woff2'),
url('#{$fonts-path}/IBM_Plex_Mono/IBMPlexMono-Regular.woff') format('woff');
font-weight: $font-weight-400;
font-style: normal;
font-display: swap;
}

35
src/app/styles/index.scss Normal file
View File

@ -0,0 +1,35 @@
@import './vars';
@import './fonts';
html {
background-color: $color-background-primary;
color: $color-text-primary;
font-family: $font-family-inter, $font-family-default;
font-size: $font-size-16;
line-height: $line-height-20;
}
html,
body {
width: 100%;
margin: 0;
padding: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: 'pnum' on, 'lnum' on;
}
h1,
h2,
h3,
h4,
h5,
h6,
p {
margin: 0;
}
a {
text-decoration: none;
color: inherit;
}

220
src/app/styles/mixins.scss Normal file
View File

@ -0,0 +1,220 @@
@use 'sass:list';
/* positions */
@mixin position-absolute-full-screen {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
/* size */
@mixin size($size) {
min-width: $size;
max-width: $size;
min-height: $size;
max-height: $size;
}
/* placeholder */
@mixin placeholder {
&::placeholder {
@content;
}
}
/* buttons */
@mixin reset-button {
align-items: flex-start;
margin: 0;
padding: 0;
text-align: center;
text-decoration: none;
text-indent: 0;
letter-spacing: inherit;
word-spacing: inherit;
text-transform: none;
color: inherit;
border: none;
outline: none;
background: none;
text-shadow: none;
font: inherit;
line-height: inherit;
cursor: default;
box-sizing: border-box;
&:focus {
outline: none;
}
}
@mixin reset-default-input {
padding: unset;
border: none;
&:focus {
outline: none;
outline-offset: initial;
}
}
/* Хак для того, чтобы убрать браузерные autofill стили */
@mixin remove-autofill-style {
&:-webkit-autofill {
/* stylelint-disable-next-line */
-webkit-background-clip: text;
}
}
/* links */
@mixin link-reset {
all: unset;
}
@mixin focus-visible {
/* stylelint-disable-next-line */
&:global(.focus-visible) {
@content;
}
}
@mixin line-clamp($n) {
display: -webkit-box;
overflow: hidden;
-webkit-line-clamp: $n;
-webkit-box-orient: vertical;
}
@mixin hide-default-input {
position: absolute;
z-index: -1;
top: 0;
left: 0;
width: 0;
height: 0;
opacity: 0;
}
/* stylelint-disable order/order */
@mixin transition($properties...) {
$declarations: ();
@each $declaration in $properties {
$duration: 0.2s;
$delay: false;
$timing-function: ease;
$property: all;
@if type-of($declaration) == 'map' {
$duration: if(map_get($declaration, 'duration'), #{map_get($declaration, 'duration')}, $duration);
$delay: if(map_get($declaration, 'delay'), #{map_get($declaration, 'delay')}, $delay);
$timing-function: if(
map_get($declaration, 'timing-function'),
#{map_get($declaration, 'timing-function')},
$timing-function
);
$property: if(map_get($declaration, 'property'), #{map_get($declaration, 'property')}, $property);
} @else {
$property: $declaration;
}
$delay-value: if($delay, ' ' + $delay, '');
$declarations: list.append(
$declarations,
#{$property + ' ' + $duration + $delay-value + ' ' + $timing-function},
comma
);
}
transition: $declarations;
}
/* stylelint-enable order/order */
@mixin flex {
display: flex;
flex-direction: row;
align-items: baseline;
justify-content: flex-start;
}
@mixin flex-center {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
@mixin flex-between {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
@mixin flex-middle {
display: flex;
flex-direction: row;
align-items: center;
}
@mixin flex-col {
display: flex;
flex-direction: column;
}
@mixin flex-col-middle {
display: flex;
flex-direction: column;
align-items: center;
}
@mixin text-overflow {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
/* scrollbar */
$scrollbar-width: 12px;
@mixin scrollbar {
&::-webkit-scrollbar {
width: $scrollbar-width;
height: $scrollbar-width;
}
&::-webkit-scrollbar-thumb {
border: 4px solid transparent;
border-radius: 6px;
background-color: $color-on-surface-light-400;
background-clip: content-box;
}
&::-webkit-scrollbar-track {
background-color: transparent;
}
&::-webkit-scrollbar-thumb:hover {
background-color: $color-on-surface-light-300;
}
}
@mixin hide-scrollbar {
overflow-y: auto;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none; /* for Chrome, Safari, and Opera */
}
}

View File

@ -0,0 +1,7 @@
$radius-none: 0;
$radius-small: 4px;
$radius-medium-x: 6px;
$radius-medium: 8px;
$radius-large: 12px;
$radius-large-x: 16px;
$radius-large-xx: 24px;

View File

@ -0,0 +1,7 @@
$shadow-dropdown: 0 1px 24px 0 rgb(0 0 0 / 40%);
$shadow-hover: 0 4px 24px 0 rgb(0 0 0 / 24%);
$shadow-bottom-item: 0 -2px 12px 0 rgb(0 0 0 / 25%);
$shadow-top: 0 -4px 1px rgb(20 20 20 / 8%), 0 -12px 6px rgb(20 20 20 / 16%), 0 -8px 20px rgb(20 20 20 / 24%);
$shadow-left: -4px 0 1px rgb(20 20 20 / 8%), -12px 0 6px rgb(20 20 20 / 16%), -8px 0 20px rgb(20 20 20 / 24%);
$shadow-right: 4px 0 1px rgb(20 20 20 / 8%), 12px 0 6px rgb(20 20 20 / 16%), 8px 0 20px rgb(20 20 20 / 24%);
$shadow-short: 0 6px 16px -12px rgb(23 40 77 / 6%);

View File

@ -0,0 +1,12 @@
$spacing-small-4x: 4px;
$spacing-small-3x: 8px;
$spacing-small-2x: 12px;
$spacing-small-x: 16px;
$spacing-small: 24px;
$spacing-medium: 32px;
$spacing-medium-x: 40px;
$spacing-large: 48px;
$spacing-large-x: 64px;
$spacing-large-2x: 72px;
$spacing-large-3x: 96px;
$spacing-large-4x: 124px;

10
src/app/styles/vars.scss Normal file
View File

@ -0,0 +1,10 @@
@import 'colors';
@import 'baseText';
@import 'breakpoints';
@import 'mixins';
@import 'radius';
@import 'spacing';
@import 'z-index';
@import 'shadow';
$header-height: 64px;

View File

@ -0,0 +1,11 @@
$z-index-behind: -1;
$z-index-default: 0;
$z-index-primary: 1;
$z-index-secondary: 2;
$z-index-dropdown-menu: 10;
$z-index-sidebar: 100;
$z-index-editor: 100;
$z-index-overlay: 101;
$z-index-modal: 1000;
$z-index-notification: 1001;
$z-index-max: 99999;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3C11.4474 3 10.9994 3.448 10.9994 4.00065C10.9994 4.55329 11.4474 5.00129 12 5.00129C15.8653 5.00129 18.9987 8.13472 18.9987 12C18.9987 15.8653 15.8653 18.9987 12 18.9987C8.13472 18.9987 5.00129 15.8653 5.00129 12C5.00129 11.4474 4.55329 10.9994 4.00065 10.9994C3.448 10.9994 3 11.4474 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 556 B

View File

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.834 9.99983H4.16731" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6673 14.1665L15.834 9.99983" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.6673 5.83317L15.834 9.99983" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 494 B

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 14.25C3.41421 14.25 3.75 14.5858 3.75 15C3.75 16.4354 3.75159 17.4365 3.85315 18.1919C3.9518 18.9257 4.13225 19.3142 4.40901 19.591C4.68577 19.8678 5.07435 20.0482 5.80812 20.1469C6.56347 20.2484 7.56459 20.25 9 20.25H15C16.4354 20.25 17.4365 20.2484 18.1919 20.1469C18.9257 20.0482 19.3142 19.8678 19.591 19.591C19.8678 19.3142 20.0482 18.9257 20.1469 18.1919C20.2484 17.4365 20.25 16.4354 20.25 15C20.25 14.5858 20.5858 14.25 21 14.25C21.4142 14.25 21.75 14.5858 21.75 15V15.0549C21.75 16.4225 21.75 17.5248 21.6335 18.3918C21.5125 19.2919 21.2536 20.0497 20.6517 20.6516C20.0497 21.2536 19.2919 21.5125 18.3918 21.6335C17.5248 21.75 16.4225 21.75 15.0549 21.75H8.94513C7.57754 21.75 6.47522 21.75 5.60825 21.6335C4.70814 21.5125 3.95027 21.2536 3.34835 20.6517C2.74643 20.0497 2.48754 19.2919 2.36652 18.3918C2.24996 17.5248 2.24998 16.4225 2.25 15.0549C2.25 15.0366 2.25 15.0183 2.25 15C2.25 14.5858 2.58579 14.25 3 14.25Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2.25C12.2106 2.25 12.4114 2.33852 12.5535 2.49392L16.5535 6.86892C16.833 7.17462 16.8118 7.64902 16.5061 7.92852C16.2004 8.20802 15.726 8.18678 15.4465 7.88108L12.75 4.9318V16C12.75 16.4142 12.4142 16.75 12 16.75C11.5858 16.75 11.25 16.4142 11.25 16V4.9318L8.55353 7.88108C8.27403 8.18678 7.79963 8.20802 7.49393 7.92852C7.18823 7.64902 7.16698 7.17462 7.44648 6.86892L11.4465 2.49392C11.5886 2.33852 11.7894 2.25 12 2.25Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Some files were not shown because too many files have changed in this diff Show More