Merge pull request 'admin-panel' (#33) from admin-panel into master
Reviewed-on: http://git.plannaplan.pl/y0rune/frontend/pulls/33 Reviewed-by: Maciej <glowackimaciej97@gmail.com>
@ -1,2 +0,0 @@
|
|||||||
node_modules
|
|
||||||
build
|
|
28
.eslintrc.js
@ -1,28 +0,0 @@
|
|||||||
// module.exports = {
|
|
||||||
// parser: '@typescript-eslint/parser',
|
|
||||||
// extends: [
|
|
||||||
// 'plugin:react/recommended',
|
|
||||||
// 'plugin:@typescript-eslint/recommended',
|
|
||||||
// 'prettier/@typescript-eslint',
|
|
||||||
// 'plugin:prettier/recommended',
|
|
||||||
// 'plugin:react-hooks/recommended',
|
|
||||||
// ],
|
|
||||||
// parserOptions: {
|
|
||||||
// ecmaVersion: 2020,
|
|
||||||
// sourceType: 'module',
|
|
||||||
// ecmaFeatures: {
|
|
||||||
// jsx: true,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// rules: {
|
|
||||||
// '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_|^req|^next' }],
|
|
||||||
// '@typescript-eslint/no-explicit-any': 0,
|
|
||||||
// '@typescript-eslint/explicit-function-return-type': 0,
|
|
||||||
// 'react/prop-types': 0,
|
|
||||||
// },
|
|
||||||
// settings: {
|
|
||||||
// react: {
|
|
||||||
// version: 'detect',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// };
|
|
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
.env
|
||||||
|
build
|
15
.vscode/launch.json
vendored
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Launch Chrome against localhost",
|
|
||||||
"url": "http://localhost:3000",
|
|
||||||
"webRoot": "${workspaceFolder}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"files": {
|
|
||||||
"main.js": "/static/js/main.993a2e72.chunk.js",
|
|
||||||
"main.js.map": "/static/js/main.993a2e72.chunk.js.map",
|
|
||||||
"runtime-main.js": "/static/js/runtime-main.38573804.js",
|
|
||||||
"runtime-main.js.map": "/static/js/runtime-main.38573804.js.map",
|
|
||||||
"static/js/2.f941ed96.chunk.js": "/static/js/2.f941ed96.chunk.js",
|
|
||||||
"static/js/2.f941ed96.chunk.js.map": "/static/js/2.f941ed96.chunk.js.map",
|
|
||||||
"index.html": "/index.html",
|
|
||||||
"precache-manifest.36f396009b702c50e7d07732240c69e5.js": "/precache-manifest.36f396009b702c50e7d07732240c69e5.js",
|
|
||||||
"service-worker.js": "/service-worker.js",
|
|
||||||
"static/js/2.f941ed96.chunk.js.LICENSE.txt": "/static/js/2.f941ed96.chunk.js.LICENSE.txt",
|
|
||||||
"static/media/PL.png": "/static/media/PL.6e9ee893.png",
|
|
||||||
"static/media/UK.png": "/static/media/UK.b4dad475.png",
|
|
||||||
"static/media/close.svg": "/static/media/close.464128e7.svg",
|
|
||||||
"static/media/search.svg": "/static/media/search.fa0d12ae.svg",
|
|
||||||
"static/media/user.png": "/static/media/user.4ba6e2a4.png"
|
|
||||||
},
|
|
||||||
"entrypoints": [
|
|
||||||
"static/js/runtime-main.38573804.js",
|
|
||||||
"static/js/2.f941ed96.chunk.js",
|
|
||||||
"static/js/main.993a2e72.chunk.js"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
<!doctype html><html lang="pl"><head><meta charset="utf-8"/><link rel="icon" href="/logo.svg"/><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="/logo.svg"/><link rel="manifest" href="/manifest.json"/><title>PlanNaPlan</title></head><body><noscript>Potrzebujesz włączyć JavaScript, żeby otworzyć tę aplikację<br>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,a=r[0],p=r[1],f=r[2],c=0,s=[];c<a.length;c++)l=a[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in p)Object.prototype.hasOwnProperty.call(p,n)&&(e[n]=p[n]);for(i&&i(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var p=t[a];0!==o[p]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var a=this.webpackJsonpplannaplan=this.webpackJsonpplannaplan||[],p=a.push.bind(a);a.push=r,a=a.slice();for(var f=0;f<a.length;f++)r(a[f]);var i=p;t()}([])</script><script src="/static/js/2.f941ed96.chunk.js"></script><script src="/static/js/main.993a2e72.chunk.js"></script></body></html>
|
|
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "PlanNaPlan",
|
|
||||||
"name": "PlanNaPlan",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "logo.svg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
self.__precacheManifest = (self.__precacheManifest || []).concat([
|
|
||||||
{
|
|
||||||
"revision": "53d5f0388bed11f07f899035cb978454",
|
|
||||||
"url": "/index.html"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"revision": "4d480560b5cfff1aa5a8",
|
|
||||||
"url": "/static/js/2.f941ed96.chunk.js"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"revision": "68ba00d5eb083746d913686923a3d904",
|
|
||||||
"url": "/static/js/2.f941ed96.chunk.js.LICENSE.txt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"revision": "209120e09e641f02e991",
|
|
||||||
"url": "/static/js/main.993a2e72.chunk.js"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"revision": "c20b5bb64d57e939de77",
|
|
||||||
"url": "/static/js/runtime-main.38573804.js"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"revision": "6e9ee893741bee46177f72398176ad9e",
|
|
||||||
"url": "/static/media/PL.6e9ee893.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"revision": "b4dad47599118a028176ceb8bbbc7a02",
|
|
||||||
"url": "/static/media/UK.b4dad475.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"revision": "464128e7e5f72a712b7c752996e86b58",
|
|
||||||
"url": "/static/media/close.464128e7.svg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"revision": "fa0d12ae1b4380d1515aed6c5e5cbc07",
|
|
||||||
"url": "/static/media/search.fa0d12ae.svg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"revision": "4ba6e2a4136a8a9ce072ee9c1e403f3a",
|
|
||||||
"url": "/static/media/user.4ba6e2a4.png"
|
|
||||||
}
|
|
||||||
]);
|
|
@ -1,3 +0,0 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* Welcome to your Workbox-powered service worker!
|
|
||||||
*
|
|
||||||
* You'll need to register this file in your web app and you should
|
|
||||||
* disable HTTP caching for this file too.
|
|
||||||
* See https://goo.gl/nhQhGp
|
|
||||||
*
|
|
||||||
* The rest of the code is auto-generated. Please don't update this file
|
|
||||||
* directly; instead, make changes to your Workbox build configuration
|
|
||||||
* and re-run your build process.
|
|
||||||
* See https://goo.gl/2aRDsh
|
|
||||||
*/
|
|
||||||
|
|
||||||
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
|
|
||||||
|
|
||||||
importScripts(
|
|
||||||
"/precache-manifest.36f396009b702c50e7d07732240c69e5.js"
|
|
||||||
);
|
|
||||||
|
|
||||||
self.addEventListener('message', (event) => {
|
|
||||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
|
||||||
self.skipWaiting();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
workbox.core.clientsClaim();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
|
|
||||||
* requests for URLs in the manifest.
|
|
||||||
* See https://goo.gl/S9QRab
|
|
||||||
*/
|
|
||||||
self.__precacheManifest = [].concat(self.__precacheManifest || []);
|
|
||||||
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
|
|
||||||
|
|
||||||
workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
|
|
||||||
|
|
||||||
blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
|
|
||||||
});
|
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
object-assign
|
|
||||||
(c) Sindre Sorhus
|
|
||||||
@license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @license
|
|
||||||
* Lodash <https://lodash.com/>
|
|
||||||
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
|
|
||||||
* Released under MIT license <https://lodash.com/license>
|
|
||||||
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
|
||||||
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A better abstraction over CSS.
|
|
||||||
*
|
|
||||||
* @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
|
|
||||||
* @website https://github.com/cssinjs/jss
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @license React v0.19.1
|
|
||||||
* 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 v16.13.1
|
|
||||||
* 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 v16.13.1
|
|
||||||
* react-is.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 v16.13.1
|
|
||||||
* 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.
|
|
||||||
*/
|
|
@ -1,2 +0,0 @@
|
|||||||
!function(e){function r(r){for(var n,l,a=r[0],p=r[1],f=r[2],c=0,s=[];c<a.length;c++)l=a[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in p)Object.prototype.hasOwnProperty.call(p,n)&&(e[n]=p[n]);for(i&&i(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var p=t[a];0!==o[p]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var a=this.webpackJsonpplannaplan=this.webpackJsonpplannaplan||[],p=a.push.bind(a);a.push=r,a=a.slice();for(var f=0;f<a.length;f++)r(a[f]);var i=p;t()}([]);
|
|
||||||
//# sourceMappingURL=runtime-main.38573804.js.map
|
|
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 11 KiB |
@ -1 +0,0 @@
|
|||||||
<?xml version="1.0" ?><svg height="48" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><path d="M38 12.83l-2.83-2.83-11.17 11.17-11.17-11.17-2.83 2.83 11.17 11.17-11.17 11.17 2.83 2.83 11.17-11.17 11.17 11.17 2.83-2.83-11.17-11.17z"/><path d="M0 0h48v48h-48z" fill="none"/></svg>
|
|
Before Width: | Height: | Size: 297 B |
@ -1 +0,0 @@
|
|||||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg height="512px" id="Layer_1" style="enable-background:new 0 0 512 512;" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M344.5,298c15-23.6,23.8-51.6,23.8-81.7c0-84.1-68.1-152.3-152.1-152.3C132.1,64,64,132.2,64,216.3 c0,84.1,68.1,152.3,152.1,152.3c30.5,0,58.9-9,82.7-24.4l6.9-4.8L414.3,448l33.7-34.3L339.5,305.1L344.5,298z M301.4,131.2 c22.7,22.7,35.2,52.9,35.2,85c0,32.1-12.5,62.3-35.2,85c-22.7,22.7-52.9,35.2-85,35.2c-32.1,0-62.3-12.5-85-35.2 c-22.7-22.7-35.2-52.9-35.2-85c0-32.1,12.5-62.3,35.2-85c22.7-22.7,52.9-35.2,85-35.2C248.5,96,278.7,108.5,301.4,131.2z"/></svg>
|
|
Before Width: | Height: | Size: 808 B |
Before Width: | Height: | Size: 18 KiB |
4777
package-lock.json
generated
28
package.json
@ -4,35 +4,41 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material-ui/core": "^4.10.0",
|
"@material-ui/core": "^4.10.0",
|
||||||
|
"@material-ui/icons": "^4.9.1",
|
||||||
"@material-ui/lab": "^4.0.0-alpha.56",
|
"@material-ui/lab": "^4.0.0-alpha.56",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
|
||||||
"@testing-library/react": "^9.5.0",
|
|
||||||
"@testing-library/user-event": "^7.2.1",
|
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"notistack": "^1.0.1",
|
"notistack": "^1.0.1",
|
||||||
"react": "^16.13.1",
|
"react": "16.8.0",
|
||||||
"react-click-away-listener": "^1.4.3",
|
"react-click-away-listener": "^1.4.3",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.1",
|
||||||
"styled-components": "^5.1.1"
|
"styled-components": "^5.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^24.9.1",
|
|
||||||
"@types/lodash": "^4.14.162",
|
|
||||||
"@types/node": "^12.12.54",
|
|
||||||
"@types/react": "^16.9.46",
|
"@types/react": "^16.9.46",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@types/styled-components": "^5.1.2",
|
"@types/styled-components": "^5.1.2",
|
||||||
"prettier": "^2.0.5",
|
"@typescript-eslint/eslint-plugin": "^4.8.1",
|
||||||
|
"@typescript-eslint/parser": "^4.8.1",
|
||||||
|
"eslint-config-prettier": "^6.15.0",
|
||||||
|
"eslint-config-react-app": "^6.0.0",
|
||||||
|
"eslint-loader": "^4.0.2",
|
||||||
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
|
"eslint-plugin-react": "^7.21.5",
|
||||||
|
"prettier": "^2.2.0",
|
||||||
"typescript": "^3.9.7"
|
"typescript": "^3.9.7"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject"
|
||||||
"lint": "eslint src/*.{js,ts,tsx} --quiet --fix"
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
@ -1,24 +1,42 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="pl">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/logo.svg" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta name="description" content="Web site created using create-react-app" />
|
<meta
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo.svg" />
|
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/
|
||||||
|
-->
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@400;700&display=swap" rel="stylesheet">
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
<title>PlanNaPlan</title>
|
-->
|
||||||
</head>
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>Potrzebujesz włączyć JavaScript, żeby otworzyć tę aplikację</br>You need to enable JavaScript to run this
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
app.</noscript>
|
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
</body>
|
<!--
|
||||||
|
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>
|
</html>
|
@ -1,25 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="200" height="200" viewBox="0 0 200 200">
|
|
||||||
<defs>
|
|
||||||
<style>
|
|
||||||
.cls-1 {
|
|
||||||
fill: #6d6e71;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cls-1, .cls-2, .cls-3 {
|
|
||||||
fill-rule: evenodd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cls-2 {
|
|
||||||
fill: #f9ca24;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cls-3 {
|
|
||||||
fill: #1761a0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
<path d="M22.000,156.000 L23.000,154.000 L24.000,153.000 L25.000,152.000 L26.000,151.000 L28.000,150.000 L33.000,150.000 L35.000,151.000 L36.000,152.000 L37.000,153.000 L38.000,154.000 L39.000,156.000 L39.000,161.000 L38.000,163.000 L37.000,164.000 L36.000,165.000 L35.000,166.000 L33.000,167.000 L28.000,167.000 L26.000,166.000 L25.000,165.000 L24.000,164.000 L23.000,163.000 L22.000,161.000 L22.000,156.000 ZM26.000,156.000 L27.000,155.000 L28.000,154.000 L33.000,154.000 L34.000,155.000 L35.000,156.000 L35.000,161.000 L34.000,162.000 L33.000,163.000 L28.000,163.000 L27.000,162.000 L26.000,161.000 L26.000,156.000 Z" class="cls-1"/>
|
|
||||||
<path d="M10.000,75.000 L100.000,131.000 L190.000,75.000 L100.000,20.000 L10.000,75.000 Z" class="cls-2"/>
|
|
||||||
<path d="M84.000,52.000 L86.000,54.000 L32.000,89.000 L32.000,153.000 L29.000,153.000 L29.000,88.000 L84.000,52.000 Z" class="cls-1"/>
|
|
||||||
<path d="M45.000,102.000 L45.000,143.000 L100.000,180.000 L155.000,143.000 L155.000,102.000 L100.000,136.000 L45.000,102.000 Z" class="cls-3"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.4 KiB |
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "PlanNaPlan",
|
|
||||||
"name": "PlanNaPlan",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "logo.svg"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
1
src/assets/history.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg id="Capa_1" enable-background="new 0 0 551.13 551.13" height="512" viewBox="0 0 551.13 551.13" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m275.531 172.228-.05 120.493c0 4.575 1.816 8.948 5.046 12.177l86.198 86.181 24.354-24.354-81.153-81.136.05-113.361z"/><path d="m310.011 34.445c-121.23 0-221.563 90.033-238.367 206.674h-71.644l86.114 86.114 86.114-86.114h-65.78c16.477-97.589 101.355-172.228 203.563-172.228 113.966 0 206.674 92.707 206.674 206.674s-92.707 206.674-206.674 206.674c-64.064 0-123.469-28.996-162.978-79.555l-27.146 21.192c46.084 58.968 115.379 92.808 190.124 92.808 132.955 0 241.119-108.181 241.119-241.119s-108.164-241.119-241.119-241.12z"/></svg>
|
After Width: | Height: | Size: 684 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
1
src/assets/plan.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg enable-background="new 0 0 24 24" height="512" viewBox="0 0 24 24" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m6 0h-5c-.552 0-1 .448-1 1v5c0 .552.448 1 1 1h5c.552 0 1-.448 1-1v-5c0-.552-.448-1-1-1z"/><path d="m15.5 6v-5c0-.552-.448-1-1-1h-5c-.552 0-1 .448-1 1v5c0 .552.448 1 1 1h5c.552 0 1-.448 1-1z"/><path d="m15.5 9.5c0-.552-.448-1-1-1h-6-1.5-6c-.552 0-1 .448-1 1v5c0 .552.448 1 1 1h13.5c.552 0 1-.448 1-1z"/><path d="m23 0h-5c-.552 0-1 .448-1 1v5c0 .552.448 1 1 1h5c.552 0 1-.448 1-1v-5c0-.552-.448-1-1-1z"/><path d="m0 18v5c0 .552.448 1 1 1h13.5c.552 0 1-.448 1-1v-5c0-.552-.448-1-1-1h-13.5c-.552 0-1 .448-1 1z"/><path d="m18 15.5h5c.552 0 1-.448 1-1v-5c0-.552-.448-1-1-1h-5c-.552 0-1 .448-1 1v5c0 .552.448 1 1 1z"/><path d="m18 24h5c.552 0 1-.448 1-1v-5c0-.552-.448-1-1-1h-5c-.552 0-1 .448-1 1v5c0 .552.448 1 1 1z"/></svg>
|
After Width: | Height: | Size: 846 B |
53
src/assets/statistics.svg
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 478 478" style="enable-background:new 0 0 478 478;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M119.5,187.75H17.1c-9.4,0-17,7.6-17.1,17.1v256c0,9.5,7.7,17.1,17.1,17.1h102.4c9.5,0,17.1-7.7,17.1-17.1v-256
|
||||||
|
C136.6,195.35,128.9,187.75,119.5,187.75z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M290.2,0.05H187.8c-9.4,0-17.1,7.6-17.1,17v443.8c0,9.5,7.7,17.1,17.1,17.1h102.4c9.5,0,17.1-7.7,17.1-17.1V17.15
|
||||||
|
C307.3,7.65,299.6,0.05,290.2,0.05z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M460.9,136.55H358.5c-9.5,0-17.1,7.6-17.1,17.1v307.2c0,9.5,7.7,17.1,17.1,17.1h102.4c9.5,0,17.1-7.7,17.1-17.1v-307.2
|
||||||
|
C478,144.15,470.3,136.55,460.9,136.55z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
133
src/components/Admin.tsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import React, { useState, MouseEvent } from 'react';
|
||||||
|
import styled from 'styled-components/macro';
|
||||||
|
import Plan from '../assets/plan.svg';
|
||||||
|
import History from '../assets/history.svg';
|
||||||
|
import Statistics from '../assets/statistics.svg';
|
||||||
|
import { Scheduler } from './Scheduler';
|
||||||
|
import { Rightbar } from './Rightbar';
|
||||||
|
|
||||||
|
const LeftSide = styled.div`
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: white;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Wrap = styled.div`
|
||||||
|
display: flex;
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
background-color: #eceef4;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
flex: 12;
|
||||||
|
display: flex;
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
background-color: #eceef4;
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface LeftPanelElement {
|
||||||
|
isCurrentTab: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LeftPanelElement = styled.div<LeftPanelElement>`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
//box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.75);
|
||||||
|
padding: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: ${({ isCurrentTab }) => (isCurrentTab === true ? `inset 0px 0px 26px 0px rgba(0,0,0,0.55)` : '')};
|
||||||
|
border-bottom: 1px solid #979797;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const HistoryDiv = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
margin-left: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
background-color: red;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StatsDiv = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
margin-left: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
background-color: blue;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LogoWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex: 2;
|
||||||
|
margin-left: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Text = styled.div`
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 5rem;
|
||||||
|
user-select: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Logo = styled.img`
|
||||||
|
width: 500px;
|
||||||
|
height: 500px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Icon = styled.img`
|
||||||
|
width: 40px;
|
||||||
|
margin: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Admin = () => {
|
||||||
|
const [currentTab, setCurrentTab] = useState<null | number>(null);
|
||||||
|
|
||||||
|
const handleClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
setCurrentTab(Number(e.currentTarget.id));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrap>
|
||||||
|
<LeftSide>
|
||||||
|
<LeftPanelElement id={'1'} isCurrentTab={currentTab === 1} onClick={handleClick}>
|
||||||
|
<Icon alt="profile" src={Plan} />
|
||||||
|
Pokaż plan
|
||||||
|
</LeftPanelElement>
|
||||||
|
<LeftPanelElement id={'2'} isCurrentTab={currentTab === 2} onClick={handleClick}>
|
||||||
|
<Icon alt="history" src={History} />
|
||||||
|
Historia Zmian
|
||||||
|
</LeftPanelElement>
|
||||||
|
<LeftPanelElement id={'3'} isCurrentTab={currentTab === 3} onClick={handleClick}>
|
||||||
|
<Icon alt="statistics" src={Statistics} />
|
||||||
|
Statystyki
|
||||||
|
</LeftPanelElement>
|
||||||
|
</LeftSide>
|
||||||
|
<Wrapper>
|
||||||
|
{currentTab === 1 ? (
|
||||||
|
<>
|
||||||
|
<Scheduler />
|
||||||
|
<Rightbar />
|
||||||
|
</>
|
||||||
|
) : currentTab === 2 ? (
|
||||||
|
<HistoryDiv />
|
||||||
|
) : currentTab === 3 ? (
|
||||||
|
<StatsDiv />
|
||||||
|
) : (
|
||||||
|
<LogoWrapper>
|
||||||
|
<Logo alt="logo" src="https://plannaplan.pl/img/logo.svg" />
|
||||||
|
<Text> plan na plan </Text>
|
||||||
|
</LogoWrapper>
|
||||||
|
)}
|
||||||
|
</Wrapper>
|
||||||
|
</Wrap>
|
||||||
|
);
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useContext } from 'react';
|
import React, { useState } from 'react';
|
||||||
import Topbar from './Topbar';
|
import Topbar from './Topbar';
|
||||||
import { Transfer } from './Transfer';
|
import { Transfer } from './Transfer';
|
||||||
|
import { Admin } from './Admin';
|
||||||
import { Scheduler } from './Scheduler';
|
import { Scheduler } from './Scheduler';
|
||||||
import { Rightbar } from './Rightbar';
|
import { Rightbar } from './Rightbar';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
@ -8,8 +9,10 @@ import styled from 'styled-components';
|
|||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
height: calc(100vh - 80px);
|
height: calc(100vh - 80px);
|
||||||
background-color: #ECEEF4;
|
background-color: #eceef4;
|
||||||
padding: 20px;
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
@ -24,6 +27,7 @@ export const App = () => {
|
|||||||
<Topbar handleTransfer={handleTransfer} />
|
<Topbar handleTransfer={handleTransfer} />
|
||||||
<Transfer isOpen={isOpenTransfer} handleClose={handleTransfer} />
|
<Transfer isOpen={isOpenTransfer} handleClose={handleTransfer} />
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
|
{/* <Admin/> */}
|
||||||
<Scheduler />
|
<Scheduler />
|
||||||
<Rightbar />
|
<Rightbar />
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import React, { useState, useContext, MouseEvent } from 'react';
|
import React, { useState, useContext } from 'react';
|
||||||
import Collapse from '@material-ui/core/Collapse';
|
import Collapse from '@material-ui/core/Collapse';
|
||||||
import { ReactComponent as Expand } from '../assets/expand.svg';
|
import { ReactComponent as Expand } from '../assets/expand.svg';
|
||||||
import { Course, Group } from '../types/index';
|
import { Course, Group, GroupType } from '../types/index';
|
||||||
import { coursesContext } from '../contexts/CoursesProvider';
|
import { coursesContext } from '../contexts/CoursesProvider';
|
||||||
import styled from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import { ReactComponent as Bin } from '../assets/bin.svg';
|
import DeleteIcon from '@material-ui/icons/Delete';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
const CourseCardWrapper = styled.div`
|
const CourseCardWrapper = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
background-color: rgb(100, 181, 246);
|
background-color: #b5d2e0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -22,19 +23,18 @@ const CourseCardWrapper = styled.div`
|
|||||||
box-shadow: 9px 9px 8px -2px rgba(0, 0, 0, 0.59);
|
box-shadow: 9px 9px 8px -2px rgba(0, 0, 0, 0.59);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
const TitleWrapper = styled.div`
|
const TitleWrapper = styled.div`
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 550;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 10px;
|
padding: 10px 10px 10px 2px;
|
||||||
`
|
`;
|
||||||
|
|
||||||
const BinIcon = styled(Bin)`
|
const BinIcon = styled(DeleteIcon)`
|
||||||
width: 20px;
|
max-width: 30px;
|
||||||
height: 20px;
|
min-width: 30px;
|
||||||
max-width: 20px;
|
|
||||||
min-width: 20px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
fill: white;
|
fill: white;
|
||||||
@ -51,37 +51,63 @@ const CourseName = styled.div`
|
|||||||
const ClassGroupStyled = styled.div`
|
const ClassGroupStyled = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
padding-bottom: 1px;
|
padding-bottom: 5px;
|
||||||
:hover {
|
:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #9ed3ff;
|
background-color: #9ed3ff;
|
||||||
}
|
}
|
||||||
|
:last-child {
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface ExpandIconProps {
|
interface ExpandIconProps {
|
||||||
isSelected: boolean;
|
selected: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExpandIcon = styled(Expand) <ExpandIconProps>`
|
export const ExpandIcon = styled(Expand)<ExpandIconProps>`
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
max-width: 20px;
|
max-width: 20px;
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
transform: ${({ isSelected }) => (isSelected ? 'scaleY(-1);' : 'scaleY(1);')};
|
transform: ${({ selected }) => (selected ? 'scaleY(-1);' : 'scaleY(1);')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TypeClass = styled.div`
|
type StyledGroupTypeProps = {
|
||||||
|
groupType: GroupType;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledGroupType = styled.div<StyledGroupTypeProps>`
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
background-color: #00506b;
|
background-color: ${({ groupType }) => (groupType === 'CLASS' ? '#FFDC61' : '#9ed3ff')};
|
||||||
border: 2px solid;
|
border: 2px solid white;
|
||||||
min-width: 45px;
|
min-width: 45px;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
color: white;
|
color: black;
|
||||||
font-weight: bold;
|
`;
|
||||||
|
|
||||||
|
const FlexboxWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type FlexItemProps = {
|
||||||
|
justifyContent?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FlexItem = styled.div<FlexItemProps>`
|
||||||
|
display: flex;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
${({ justifyContent }) =>
|
||||||
|
justifyContent &&
|
||||||
|
css`
|
||||||
|
justify-content: ${justifyContent};
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
@ -89,51 +115,100 @@ const useStyles = makeStyles({
|
|||||||
maxHeight: '244px',
|
maxHeight: '244px',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
'&::-webkit-scrollbar': {
|
'&::-webkit-scrollbar': {
|
||||||
width: '0.4em',
|
width: '0.3em',
|
||||||
|
borderStyle: 'none',
|
||||||
},
|
},
|
||||||
'&::-webkit-scrollbar-track': {
|
'&::-webkit-scrollbar-track': {
|
||||||
'-webkit-box-shadow': 'inset 0 0 6px rgba(1,0,0,0.1)',
|
borderRadius: '10px',
|
||||||
},
|
},
|
||||||
'&::-webkit-scrollbar-thumb': {
|
'&::-webkit-scrollbar-thumb': {
|
||||||
borderRadius: '10px',
|
borderRadius: '10px',
|
||||||
backgroundColor: '#d4b851',
|
backgroundColor: '#4b4b4b',
|
||||||
outline: '1px solid slategrey',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface CourseCardProps {
|
interface CourseCardProps {
|
||||||
course: Course;
|
course: Course;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CourseCard = ({ course }: CourseCardProps) => {
|
export const CourseCard = ({ course }: CourseCardProps) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const { addGroup, deleteFromBasket } = useContext(coursesContext)!;
|
const {
|
||||||
|
hoveredGroup,
|
||||||
|
changeGroupInBasket,
|
||||||
|
deleteFromBasket,
|
||||||
|
selectBasketCourseGroups,
|
||||||
|
changeHoveredGroup,
|
||||||
|
} = useContext(coursesContext)!;
|
||||||
const [isSelected, setSelected] = useState(false);
|
const [isSelected, setSelected] = useState(false);
|
||||||
const groups = course.lectures === undefined ? course.classes : [...course.lectures, ...course.classes];
|
const groups = [...course.lectures!, ...course.classes!];
|
||||||
|
const basketCourseGroups = useMemo(() => selectBasketCourseGroups(course.id), []);
|
||||||
const onGroupClick = (group: Group, id: number) => addGroup(group, id);
|
const [previous, setPrevious] = useState(basketCourseGroups);
|
||||||
|
// console.log('lecture is: ', courseLecture);
|
||||||
|
// console.log('class is: ', courseClasses);
|
||||||
|
const onGroupClick = (group: Group, courseId: number) => {
|
||||||
|
setPrevious((prev) => (group.type === GroupType.CLASS ? { ...prev, classes: group } : { ...prev, lecture: group }));
|
||||||
|
changeGroupInBasket(group, courseId);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CourseCardWrapper>
|
<CourseCardWrapper>
|
||||||
<TitleWrapper>
|
<TitleWrapper onClick={() => setSelected(!isSelected)}>
|
||||||
<BinIcon onClick={() => deleteFromBasket(course.id)}></BinIcon>
|
<BinIcon
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
deleteFromBasket(course.id);
|
||||||
|
setSelected(false);
|
||||||
|
}}
|
||||||
|
></BinIcon>
|
||||||
<CourseName onClick={() => setSelected(!isSelected)}>{course.name}</CourseName>
|
<CourseName onClick={() => setSelected(!isSelected)}>{course.name}</CourseName>
|
||||||
<ExpandIcon onClick={() => setSelected(!isSelected)} isSelected={isSelected} />
|
<ExpandIcon onClick={() => setSelected(!isSelected)} selected={isSelected} />
|
||||||
</TitleWrapper>
|
</TitleWrapper>
|
||||||
<Collapse className={classes.expanded} in={isSelected} timeout="auto" unmountOnExit>
|
<Collapse className={classes.expanded} in={isSelected} timeout="auto" unmountOnExit>
|
||||||
{groups
|
{groups.map((group: Group, index) => (
|
||||||
.sort((a, b) => b.type.localeCompare(a.type))
|
<ClassGroupStyled
|
||||||
.map((group, index) => (
|
key={index}
|
||||||
<ClassGroupStyled key={index} onClick={() => onGroupClick(group, course.id)}>
|
onClick={() => onGroupClick(group, course.id)}
|
||||||
<TypeClass>{group.type === 'CLASS' ? 'Ćw.' : 'Wyk.'}</TypeClass>
|
onMouseEnter={() => {
|
||||||
<p>
|
if (group.type === GroupType.CLASS) {
|
||||||
{group.time} {group.room} <br></br> {group.lecturer}
|
changeGroupInBasket(group, course.id);
|
||||||
</p>
|
// setTimeout(()=> { changeHoveredGroup(courseClasses)},[500])
|
||||||
</ClassGroupStyled>
|
}
|
||||||
))}
|
if (group.type === GroupType.LECTURE) {
|
||||||
|
changeGroupInBasket(group, course.id);
|
||||||
|
// setTimeout(()=> { changeHoveredGroup(courseLecture)},[500])
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
if (hoveredGroup) {
|
||||||
|
if (hoveredGroup.type === GroupType.CLASS && previous.classes !== undefined) {
|
||||||
|
changeGroupInBasket(previous.classes, course.id);
|
||||||
|
}
|
||||||
|
if (hoveredGroup.type === GroupType.LECTURE && previous.lecture !== undefined) {
|
||||||
|
changeGroupInBasket(previous.lecture, course.id);
|
||||||
|
}
|
||||||
|
changeHoveredGroup(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<StyledGroupType groupType={group.type}>{group.type === 'CLASS' ? 'ĆW' : 'WYK'}</StyledGroupType>
|
||||||
|
<FlexboxWrapper>
|
||||||
|
{group.lecturer.replace('UAM', '').length >= 32 ? (
|
||||||
|
<FlexItem style={{ justifyContent: 'center', marginLeft: '40px' }}>
|
||||||
|
{group.lecturer.replace('UAM', '')}
|
||||||
|
</FlexItem>
|
||||||
|
) : (
|
||||||
|
<FlexItem style={{ justifyContent: 'center', marginLeft: '10px' }}>
|
||||||
|
{group.lecturer.replace('UAM', '')}
|
||||||
|
</FlexItem>
|
||||||
|
)}
|
||||||
|
<FlexItem style={{ justifyContent: 'center', margin: '0 50px' }}>
|
||||||
|
<span>{/*group.time*/}</span> <span> Sala: {group.room}</span>
|
||||||
|
</FlexItem>
|
||||||
|
</FlexboxWrapper>
|
||||||
|
</ClassGroupStyled>
|
||||||
|
))}
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</CourseCardWrapper>
|
</CourseCardWrapper>
|
||||||
);
|
);
|
||||||
|
@ -1,31 +1,30 @@
|
|||||||
import React, { useState, useContext, useEffect, MouseEvent, forwardRef } from 'react';
|
import React, { useState, useContext, useEffect, MouseEvent, useMemo } from 'react';
|
||||||
import { coursesContext } from '../contexts/CoursesProvider';
|
import { coursesContext } from '../contexts/CoursesProvider';
|
||||||
import { Course } from '../types';
|
import { Course } from '../types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const DropdownContainer = styled.div`
|
const DropdownContainer = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 99999999;
|
z-index: 99999999;
|
||||||
max-height: 420px;
|
max-height: 396px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
opacity: 0.97;
|
||||||
box-shadow: 0.05em 0.2em 0.6em rgba(0, 0, 0, 0.2);
|
box-shadow: 0.05em 0.2em 0.6em rgba(0, 0, 0, 0.2);
|
||||||
scroll-snap-type: y mandatory;
|
scroll-snap-type: y mandatory;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
|
background-color: #f2f4f7;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 12px;
|
background-color: #f2f4f7;
|
||||||
background-color: #f5f5f5;
|
width: 5px;
|
||||||
|
border-style: none;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: black;
|
background-color: #4b4b4b;
|
||||||
border: 1px solid;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ const CourseContainer = styled.div`
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
background-color: #f2f4f7;
|
background-color: #f2f4f7;
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
scroll-snap-align: end;
|
scroll-snap-align: end;
|
||||||
:hover {
|
:hover {
|
||||||
@ -48,27 +47,22 @@ interface DropdownProps {
|
|||||||
handleCloseDropdown: () => void;
|
handleCloseDropdown: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Dropdown = forwardRef(({ open, input, handleCloseDropdown }: DropdownProps, ref: any) => {
|
export const Dropdown = ({ open, input, handleCloseDropdown }: DropdownProps) => {
|
||||||
//courses - choosenCourses
|
const { courses, selectBasketNames, addCourseToBasket } = useContext(coursesContext)!;
|
||||||
|
const basketNames = useMemo(() => selectBasketNames(), [selectBasketNames]);
|
||||||
const [filteredCourses, setFilteredCourses] = useState<Array<Course>>([]);
|
const [filteredCourses, setFilteredCourses] = useState<Array<Course>>([]);
|
||||||
|
|
||||||
const { courses, basket, addToBasket } = useContext(coursesContext)!;
|
const onCourseClick = (event: MouseEvent) => {
|
||||||
|
const target = event.currentTarget;
|
||||||
useEffect(() => {
|
if (target.id && target.textContent) {
|
||||||
console.log('wut');
|
const course = filteredCourses.find(({ id }) => id.toString() === target.id)!;
|
||||||
}, [open, input, handleCloseDropdown]);
|
addCourseToBasket(course);
|
||||||
|
handleCloseDropdown();
|
||||||
useEffect(() => {
|
}
|
||||||
console.log('input is: ', input);
|
};
|
||||||
}, [input]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log('is open: ', open);
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const filterCourses = (input: string) => {
|
const filterCourses = (input: string) => {
|
||||||
const choosenCoursesNames = basket.map(({ name }) => name.trim());
|
|
||||||
const filteredCourses = courses.filter(
|
const filteredCourses = courses.filter(
|
||||||
({ name }) =>
|
({ name }) =>
|
||||||
name
|
name
|
||||||
@ -80,24 +74,12 @@ export const Dropdown = forwardRef(({ open, input, handleCloseDropdown }: Dropdo
|
|||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
.replace(/[\u0300-\u036f]/g, ''),
|
.replace(/[\u0300-\u036f]/g, ''),
|
||||||
) && !choosenCoursesNames.includes(name),
|
) && !basketNames.includes(name),
|
||||||
);
|
);
|
||||||
setFilteredCourses(filteredCourses);
|
setFilteredCourses(filteredCourses);
|
||||||
};
|
};
|
||||||
console.log("filtering courses");
|
|
||||||
filterCourses(input);
|
filterCourses(input);
|
||||||
}, [open, input, basket]);
|
}, [basketNames, courses, input]);
|
||||||
|
|
||||||
const onCourseClick = async (event: MouseEvent) => {
|
|
||||||
const target = event.currentTarget;
|
|
||||||
if (target.id && target.textContent) {
|
|
||||||
const course = filteredCourses.find(({ id }) => id.toString() === target.id)!;
|
|
||||||
console.log('added course is');
|
|
||||||
console.log(course);
|
|
||||||
addToBasket(course);
|
|
||||||
handleCloseDropdown();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownContainer>
|
<DropdownContainer>
|
||||||
@ -112,4 +94,4 @@ export const Dropdown = forwardRef(({ open, input, handleCloseDropdown }: Dropdo
|
|||||||
)}
|
)}
|
||||||
</DropdownContainer>
|
</DropdownContainer>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
@ -12,7 +12,7 @@ export const Profile = ({ anchorEl, handleClose }: ProfileProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
|
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
|
||||||
<MenuItem>Profil</MenuItem>
|
{/* <MenuItem>Profil</MenuItem> */}
|
||||||
<MenuItem onClick={logout}>Wyloguj</MenuItem>
|
<MenuItem onClick={logout}>Wyloguj</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
@ -2,28 +2,24 @@ import React, { useContext } from 'react';
|
|||||||
import { CourseCard } from './CourseCard';
|
import { CourseCard } from './CourseCard';
|
||||||
import { coursesContext } from '../contexts/CoursesProvider';
|
import { coursesContext } from '../contexts/CoursesProvider';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from '../utils/index';
|
||||||
|
|
||||||
const RightbarStyled = styled.div`
|
const RightbarWrapper = styled.div`
|
||||||
padding-top: 10px;
|
padding: 15px;
|
||||||
padding-left: 15px;
|
|
||||||
padding-right: 15px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 300px;
|
width: 350px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 12px;
|
width: 5px;
|
||||||
background-color: #f5f5f5;
|
border-style: none;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: black;
|
background-color: #4b4b4b;
|
||||||
border: 1px solid;
|
|
||||||
}
|
}
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -34,36 +30,34 @@ const SaveButton = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: #417cab;
|
background-color: #43a047;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
&:hover {
|
&:hover {
|
||||||
color: white;
|
color: #ffffff;
|
||||||
|
box-shadow: 0px 5px 4px 0px rgba(0, 0, 0, 0.24);
|
||||||
}
|
}
|
||||||
box-shadow: 6px 6px 6px -2px rgba(0, 0, 0, 0.59);
|
|
||||||
|
&:active {
|
||||||
|
background-color: #54c457;
|
||||||
|
}
|
||||||
|
|
||||||
|
box-shadow: 0px 3px 3px 0px rgba(0, 0, 0, 0.24);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Rightbar = () => {
|
export const Rightbar = () => {
|
||||||
const { courses, basket, saveBasket } = useContext(coursesContext)!;
|
const { selectBasketCourses, saveBasket } = useContext(coursesContext)!;
|
||||||
|
|
||||||
const getBasketGroups = () => {
|
|
||||||
const names = basket.map(({ name }) => name);
|
|
||||||
return courses.filter(({ name }) => names.includes(name));
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredCourses = getBasketGroups();
|
|
||||||
|
|
||||||
|
const basketCourses = selectBasketCourses();
|
||||||
const handleSave = debounce(() => saveBasket(), 500);
|
const handleSave = debounce(() => saveBasket(), 500);
|
||||||
|
|
||||||
//need to insert student name from db and course maybe based on current time or from db too
|
|
||||||
return (
|
return (
|
||||||
<RightbarStyled>
|
<RightbarWrapper>
|
||||||
<SaveButton onClick={handleSave}>ZAPISZ</SaveButton>
|
<SaveButton onClick={handleSave}>ZAPISZ</SaveButton>
|
||||||
{filteredCourses.map((course, index) => (
|
{basketCourses.map((course) => (
|
||||||
<CourseCard course={course} key={index} />
|
<CourseCard course={course} key={course.id} />
|
||||||
))}
|
))}
|
||||||
</RightbarStyled>
|
</RightbarWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, MouseEvent, useRef, useCallback, useLayoutEffect } from 'react';
|
import React, { useEffect, useRef } from 'react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { SchedulerEvents } from './SchedulerEvents';
|
import { SchedulerEvents } from './SchedulerEvents';
|
||||||
import { days, hours } from '../constants/index';
|
import { days, hours } from '../constants/index';
|
||||||
@ -11,6 +11,7 @@ const SchedulerWrapper = styled.div`
|
|||||||
padding: 10px 40px 25px 10px;
|
padding: 10px 40px 25px 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
margin-left: 20px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -42,9 +43,9 @@ interface TableCellProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TableCell = styled.div<TableCellProps>`
|
const TableCell = styled.div<TableCellProps>`
|
||||||
border-width: ${({ isHourColumn }) => !isHourColumn && '2px'};
|
border-width: ${({ isHourColumn }) => !isHourColumn && '1px'};
|
||||||
border-style: ${({ isHourColumn }) => !isHourColumn && 'none solid dotted none'};
|
border-style: ${({ isHourColumn }) => !isHourColumn && 'none solid dotted none'};
|
||||||
border-color: rgb(242, 243, 245);
|
border-color: rgb(235, 235, 235);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: ${({ isHourColumn }) => (isHourColumn ? 'flex-end' : 'center')};
|
justify-content: ${({ isHourColumn }) => (isHourColumn ? 'flex-end' : 'center')};
|
||||||
@ -55,7 +56,7 @@ const TableCell = styled.div<TableCellProps>`
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
:nth-child(2) {
|
:nth-child(2) {
|
||||||
border-left: 2px solid rgb(242, 243, 245);
|
border-left: 1px solid rgb(235, 235, 235);
|
||||||
}
|
}
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
`;
|
`;
|
||||||
@ -63,18 +64,13 @@ const TableCell = styled.div<TableCellProps>`
|
|||||||
export const Scheduler = () => {
|
export const Scheduler = () => {
|
||||||
const cellRef = useRef<HTMLDivElement>(null);
|
const cellRef = useRef<HTMLDivElement>(null);
|
||||||
const [cellWidth, setCellWidth] = useState(0);
|
const [cellWidth, setCellWidth] = useState(0);
|
||||||
const [cellTop, setCellTop] = useState(0);
|
|
||||||
const [cellHeight, setCellHeight] = useState(0);
|
const [cellHeight, setCellHeight] = useState(0);
|
||||||
|
|
||||||
console.log('cell height: ', cellHeight);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
if (cellRef.current) {
|
if (cellRef.current) {
|
||||||
setCellWidth(cellRef.current.getBoundingClientRect().width);
|
setCellWidth(cellRef.current.getBoundingClientRect().width);
|
||||||
setCellTop(cellRef.current.getBoundingClientRect().top);
|
|
||||||
setCellHeight(cellRef.current.getBoundingClientRect().height);
|
setCellHeight(cellRef.current.getBoundingClientRect().height);
|
||||||
cellRef.current.style.backgroundColor = 'blue';
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
handleResize();
|
handleResize();
|
||||||
@ -83,52 +79,52 @@ export const Scheduler = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<SchedulerWrapper>
|
||||||
<SchedulerWrapper>
|
<TableHead>
|
||||||
<TableHead>
|
{days.map((day, indexCell) =>
|
||||||
{days.map((day, indexCell) =>
|
indexCell === 0 ? (
|
||||||
indexCell === 0 ? (
|
<TableCell isHourColumn={true} key={indexCell}>
|
||||||
<TableCell isHourColumn={true} key={indexCell}>
|
{day}
|
||||||
{day}
|
</TableCell>
|
||||||
</TableCell>
|
) : (
|
||||||
) : (
|
<TableCell style={{ borderStyle: 'none none solid none' }} key={indexCell}>
|
||||||
<TableCell style={{ borderStyle: 'none none solid none' }} key={indexCell}>
|
{day}
|
||||||
{day}
|
</TableCell>
|
||||||
</TableCell>
|
),
|
||||||
),
|
)}
|
||||||
)}
|
</TableHead>
|
||||||
</TableHead>
|
<TableBody>
|
||||||
<TableBody>
|
{hours.map((hour, indexRow) => (
|
||||||
{hours.map((hour, indexRow) => (
|
<TableRow key={indexRow}>
|
||||||
<TableRow key={indexRow}>
|
{[hour, '', '', '', '', ''].map((value, indexCell) =>
|
||||||
{[hour, '', '', '', '', ''].map((value, indexCell) =>
|
indexCell === 0 ? (
|
||||||
indexCell === 0 ? (
|
<TableCell isHourColumn={true} cellHeight={cellHeight} key={`${indexRow}${indexCell}`}>
|
||||||
<TableCell isHourColumn={true} cellHeight={cellHeight} key={`${indexRow}${indexCell}`}>
|
{value}
|
||||||
{value}
|
</TableCell>
|
||||||
</TableCell>
|
) : indexRow === 0 && indexCell === 1 ? (
|
||||||
) : indexRow === 0 && indexCell === 1 ? (
|
<TableCell ref={cellRef} key={`${indexRow}${indexCell}`}>
|
||||||
<TableCell ref={cellRef} key={`${indexRow}${indexCell}`}>
|
{value}
|
||||||
{value}
|
</TableCell>
|
||||||
</TableCell>
|
) : indexRow === 23 ? (
|
||||||
) : indexRow === 23 ? (
|
<TableCell style={{ borderBottom: '1px solid rgb(235, 235, 235)' }} key={`${indexRow}${indexCell}`}>
|
||||||
<TableCell style={{ borderBottom: '2px solid rgb(242, 243, 245)' }} key={`${indexRow}${indexCell}`}>
|
{value}
|
||||||
{value}
|
</TableCell>
|
||||||
</TableCell>
|
) : indexRow === 5 ? (
|
||||||
) : indexCell === 5 ? (
|
<TableCell style={{ borderBottom: '1px solid rgb(235, 235, 235)' }} key={`${indexRow}${indexCell}`}>
|
||||||
<TableCell key={`${indexRow}${indexCell}`}>{value}</TableCell>
|
{value}
|
||||||
) : indexRow % 2 !== 0 ? (
|
</TableCell>
|
||||||
<TableCell style={{ borderBottom: '2px solid rgb(242, 243, 245)' }} key={`${indexRow}${indexCell}`}>
|
) : indexRow % 2 !== 0 ? (
|
||||||
{value}
|
<TableCell style={{ borderBottom: '1px solid rgb(235, 235, 235)' }} key={`${indexRow}${indexCell}`}>
|
||||||
</TableCell>
|
{value}
|
||||||
) : (
|
</TableCell>
|
||||||
<TableCell key={`${indexRow}${indexCell}`}>{value}</TableCell>
|
) : (
|
||||||
),
|
<TableCell key={`${indexRow}${indexCell}`}>{value}</TableCell>
|
||||||
)}
|
),
|
||||||
</TableRow>
|
)}
|
||||||
))}
|
</TableRow>
|
||||||
<SchedulerEvents cellTop={cellTop} cellWidth={cellWidth} cellHeight={cellHeight} />
|
))}
|
||||||
</TableBody>
|
<SchedulerEvents cellWidth={cellWidth} cellHeight={cellHeight} />
|
||||||
</SchedulerWrapper>
|
</TableBody>
|
||||||
</>
|
</SchedulerWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,57 +1,26 @@
|
|||||||
import React, { useContext, useEffect, useState, MouseEvent } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { SchedulerRow } from './SchedulerRow';
|
import { SchedulerRow } from './SchedulerRow';
|
||||||
import { coursesContext } from '../contexts/CoursesProvider';
|
import { coursesContext } from '../contexts/CoursesProvider';
|
||||||
import { Group, Basket } from '../types';
|
import { selectGroupsToShow } from '../utils/index';
|
||||||
|
import { ROWS_COUNT } from '../constants';
|
||||||
interface SchedulerEventsProps {
|
interface SchedulerEventsProps {
|
||||||
cellTop: number;
|
|
||||||
cellWidth: number;
|
cellWidth: number;
|
||||||
cellHeight: number;
|
cellHeight: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SchedulerEvents = ({ cellTop, cellWidth, cellHeight }: SchedulerEventsProps) => {
|
export const SchedulerEvents = ({ cellWidth, cellHeight }: SchedulerEventsProps) => {
|
||||||
const { basket } = useContext(coursesContext)!;
|
const { selectSchedulerEvents } = useContext(coursesContext)!;
|
||||||
console.log(`values: cellTop: ${cellTop}, cellWidth: ${cellWidth}, cellHeight: ${cellHeight}`);
|
|
||||||
const [choosenGroupsMappedToEvents, setChoosenGroupsMappedToEvents] = useState<any>([]);
|
|
||||||
|
|
||||||
const groupTimeToEventRowMapping: { [time: string]: number } = {
|
const schedulerEvents = selectSchedulerEvents();
|
||||||
'8.15': 0,
|
|
||||||
'10.00': 1,
|
|
||||||
'11.45': 2,
|
|
||||||
'13.45': 3,
|
|
||||||
'15.30': 4,
|
|
||||||
'17.15': 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function mapGroupTimeToEventRow(basket: Array<Basket>) {
|
|
||||||
const classes = basket.map(({ classes, name }) => ({ ...classes, name })) as Array<Group & { name: string }>;
|
|
||||||
const lectures = basket.map(({ lecture, name }) => ({ ...lecture, name })) as Array<Group & { name: string }>;
|
|
||||||
const merged = [...classes, ...lectures];
|
|
||||||
|
|
||||||
//deleted if statement, maybe it is needed
|
|
||||||
const groupsMapped = merged.map(({ id, day, lecturer, room, time, name, type }) => ({
|
|
||||||
id,
|
|
||||||
day,
|
|
||||||
lecturer,
|
|
||||||
room,
|
|
||||||
eventRow: groupTimeToEventRowMapping[time],
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
}));
|
|
||||||
setChoosenGroupsMappedToEvents(groupsMapped);
|
|
||||||
}
|
|
||||||
mapGroupTimeToEventRow(basket);
|
|
||||||
}, [basket]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{[...Array(6)].map((_, index) => (
|
{[...Array(ROWS_COUNT)].map((_, index) => (
|
||||||
<SchedulerRow
|
<SchedulerRow
|
||||||
key={index}
|
key={index}
|
||||||
groups={choosenGroupsMappedToEvents.filter((group: any) => group.eventRow === index)}
|
groups={selectGroupsToShow(schedulerEvents, index)}
|
||||||
indexRow={index}
|
indexRow={index}
|
||||||
cellTop={
|
rowTop={
|
||||||
index === 0
|
index === 0
|
||||||
? cellHeight / 2
|
? cellHeight / 2
|
||||||
: index === 1
|
: index === 1
|
||||||
|
@ -1,92 +1,163 @@
|
|||||||
import React, { MouseEvent, useState } from 'react';
|
import React, { Fragment, MouseEvent, useState, useEffect, useContext } from 'react';
|
||||||
import { Group, GroupType } from '../types';
|
import { GroupType, SchedulerEvent } from '../types';
|
||||||
import styled from 'styled-components/macro';
|
import styled, { css } from 'styled-components/macro';
|
||||||
import Popover from '@material-ui/core/Popover';
|
import Popover from '@material-ui/core/Popover';
|
||||||
import Typography from '@material-ui/core/Typography';
|
|
||||||
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
|
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
|
||||||
|
import { MONDAY_TO_FRIDAY } from '../constants';
|
||||||
|
import { coursesContext } from '../contexts/CoursesProvider';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
popover: {
|
popover: {
|
||||||
pointerEvents: 'none',
|
fontSize: '14px',
|
||||||
},
|
},
|
||||||
paper: {
|
paper: {
|
||||||
padding: theme.spacing(1),
|
padding: '15px 15px 15px 15px',
|
||||||
marginLeft: 5,
|
textAlign: 'left',
|
||||||
textAlign: 'center',
|
lineHeight: `1 !important`,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
interface ClassesWrapperProps {
|
const PopoverSpan = styled.span`
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 2px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface SchedulerEventsWrapperProps {
|
||||||
eventIndex: number;
|
eventIndex: number;
|
||||||
cellTop: number;
|
rowTop: number;
|
||||||
cellWidth: number;
|
cellWidth: number;
|
||||||
cellHeight: number;
|
cellHeight: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ClassesWrapper = styled.div<ClassesWrapperProps>`
|
const SchedulerEventsWrapper = styled.div<SchedulerEventsWrapperProps>`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
top: ${({ cellTop }) => cellTop}px;
|
top: ${({ rowTop }) => rowTop}px;
|
||||||
left: ${({ cellWidth, eventIndex }) => (cellWidth * 1) / 5 + 15 + cellWidth * eventIndex}px;
|
left: ${({ cellWidth, eventIndex }) => (cellWidth * 1) / 5 + 4 + cellWidth * eventIndex}px;
|
||||||
width: ${({ cellWidth }) => cellWidth - 10}px;
|
width: ${({ cellWidth }) => cellWidth - 10}px;
|
||||||
height: ${({ cellHeight }) => cellHeight * 3}px;
|
height: ${({ cellHeight }) => cellHeight * 3}px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface ClassesProps {
|
interface SchedulerEventProps {
|
||||||
|
cellWidth: number;
|
||||||
cellHeight: number;
|
cellHeight: number;
|
||||||
groupType: GroupType;
|
groupType: GroupType;
|
||||||
|
isHovered: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Classes = styled.div<ClassesProps>`
|
const StyledSchedulerEvent = styled.div<SchedulerEventProps>`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
z-index: 2;
|
z-index: 20000;
|
||||||
|
font-size: 0.65vw;
|
||||||
|
line-height: normal;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
height: ${({ cellHeight }) => cellHeight * 3}px;
|
height: ${({ cellHeight }) => cellHeight * 3}px;
|
||||||
|
width: ${({ cellWidth }) => (cellWidth * 3) / 4}px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
text-align: left;
|
padding: 5px 5px 0 5px;
|
||||||
background-color: ${({ groupType }) => (groupType === 'CLASS' ? '#FFDC61' : '#9ed3ff')};
|
text-align: center;
|
||||||
box-shadow: 9px 9px 8px -2px rgba(0, 0, 0, 0.59);
|
background-color: ${({ groupType, isHovered }) => {
|
||||||
|
if (isHovered) {
|
||||||
|
return groupType === 'CLASS' ? '#ffefb5' : '#d4ecff';
|
||||||
|
} else {
|
||||||
|
return groupType === 'CLASS' ? '#FFDC61' : '#9ed3ff';
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
box-shadow: 3px 3px 3px 0px rgba(0, 0, 0, 0.75);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const threeStyles = () => {
|
||||||
|
return css`
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 70px;
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BoldParagraphProps = {
|
||||||
|
isThree?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BoldParagraph = styled.p<BoldParagraphProps>`
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 3;
|
||||||
|
${({ isThree }) => isThree && threeStyles}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ClassWrap = styled.div`
|
||||||
|
font-weight: 700;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
line-height: normal;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TextWrapper = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
width: inherit;
|
||||||
|
padding: 0 3px 5px 3px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface SchedulerRowProps {
|
interface SchedulerRowProps {
|
||||||
groups: Array<Group & { name: string }>;
|
groups: Array<SchedulerEvent>;
|
||||||
indexRow: number;
|
indexRow: number;
|
||||||
cellTop: number;
|
rowTop: number;
|
||||||
cellWidth: number;
|
cellWidth: number;
|
||||||
cellHeight: number;
|
cellHeight: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SchedulerRow = ({ groups, indexRow, cellTop, cellWidth, cellHeight }: SchedulerRowProps) => {
|
const getGroupsPerDay = (groups: Array<SchedulerEvent>) => {
|
||||||
|
const groupsPerDay: any = { 0: 0, 1: 0, 2: 0, 3: 0, 4: 0 };
|
||||||
|
for (const group of groups) {
|
||||||
|
groupsPerDay[group.day]++;
|
||||||
|
}
|
||||||
|
return groupsPerDay;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SchedulerRow = ({ groups, indexRow, rowTop, cellWidth, cellHeight }: SchedulerRowProps) => {
|
||||||
|
const { hoveredGroup } = useContext(coursesContext)!;
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
const groupsPerDay = getGroupsPerDay(groups);
|
||||||
const [anchorEl, setAnchorEl] = React.useState<HTMLDivElement | null>(null);
|
const [anchorEl, setAnchorEl] = React.useState<HTMLDivElement | null>(null);
|
||||||
const [popoverId, setPopoverId] = useState<string | null>(null);
|
const [popoverId, setPopoverId] = useState<string | null>(null);
|
||||||
|
|
||||||
//looks weird
|
//looks weird
|
||||||
const handlePopoverOpen = (event: MouseEvent<HTMLDivElement, globalThis.MouseEvent>) => {
|
const handlePopoverOpen = (event: MouseEvent<HTMLDivElement, globalThis.MouseEvent>) => {
|
||||||
|
console.log('I was clicked!!!!');
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
setPopoverId(event.currentTarget.id);
|
setPopoverId(event.currentTarget.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePopoverClose = () => {
|
const handlePopoverClose = (e: MouseEvent<any>) => {
|
||||||
setAnchorEl(null);
|
console.log('current target:', e.currentTarget);
|
||||||
|
console.log(' target:', e.target);
|
||||||
setPopoverId(null);
|
setPopoverId(null);
|
||||||
|
setAnchorEl(null);
|
||||||
|
console.log('click awayyy');
|
||||||
};
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('anchorEl: ', anchorEl);
|
||||||
|
}, [anchorEl]);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
|
const id = open ? 'simple-popover' : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{[...Array(5)].map((_, eventIndex) => (
|
{[...Array(MONDAY_TO_FRIDAY)].map((_, eventIndex) => (
|
||||||
<ClassesWrapper
|
<SchedulerEventsWrapper
|
||||||
eventIndex={eventIndex}
|
eventIndex={eventIndex}
|
||||||
cellTop={cellTop}
|
rowTop={rowTop}
|
||||||
cellWidth={cellWidth}
|
cellWidth={cellWidth}
|
||||||
cellHeight={cellHeight}
|
cellHeight={cellHeight}
|
||||||
key={eventIndex}
|
key={eventIndex}
|
||||||
@ -95,27 +166,35 @@ export const SchedulerRow = ({ groups, indexRow, cellTop, cellWidth, cellHeight
|
|||||||
{groups.map(
|
{groups.map(
|
||||||
(group, index) =>
|
(group, index) =>
|
||||||
group.day === eventIndex && (
|
group.day === eventIndex && (
|
||||||
<>
|
<Fragment key={index}>
|
||||||
<Classes
|
<StyledSchedulerEvent
|
||||||
onClick={() => {
|
aria-describedby={id}
|
||||||
console.log('group: ', group);
|
isHovered={group.id === hoveredGroup?.id}
|
||||||
}}
|
|
||||||
groupType={group.type}
|
groupType={group.type}
|
||||||
|
cellWidth={cellWidth}
|
||||||
cellHeight={cellHeight}
|
cellHeight={cellHeight}
|
||||||
id={`eventRow${indexRow}eventCol${eventIndex}${index}`}
|
id={`eventRow${indexRow}eventCol${eventIndex}${index}`}
|
||||||
key={index}
|
key={index}
|
||||||
aria-owns={open ? `mouse-over-popover` : undefined}
|
aria-owns={open ? `mouse-over-popover` : undefined}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
onMouseEnter={(e) => handlePopoverOpen(e)}
|
onClick={(e) => handlePopoverOpen(e)}
|
||||||
onMouseLeave={handlePopoverClose}
|
|
||||||
>
|
>
|
||||||
<div>
|
<ClassWrap>
|
||||||
<p style={{ fontWeight: 700 }}>{groups[index].name}</p>
|
<BoldParagraph isThree={groupsPerDay[group.day] >= 3}>{groups[index].name}</BoldParagraph>
|
||||||
<p>{groups[index].room}</p>
|
{groupsPerDay[group.day] < 3 ? (
|
||||||
</div>
|
<TextWrapper>
|
||||||
</Classes>
|
<div>{`${groups[index].time[0]}-${groups[index].time[1]}`}</div>
|
||||||
|
<div>3/{groups[index].capacity}</div>
|
||||||
|
</TextWrapper>
|
||||||
|
) : (
|
||||||
|
<TextWrapper style={{ flexDirection: 'column' }}>
|
||||||
|
<div style={{ alignSelf: 'flex-end' }}>3/{groups[index].capacity}</div>
|
||||||
|
</TextWrapper>
|
||||||
|
)}
|
||||||
|
</ClassWrap>
|
||||||
|
</StyledSchedulerEvent>
|
||||||
<Popover
|
<Popover
|
||||||
id={`mouse-over-popover`}
|
id={id}
|
||||||
className={classes.popover}
|
className={classes.popover}
|
||||||
classes={{
|
classes={{
|
||||||
paper: classes.paper,
|
paper: classes.paper,
|
||||||
@ -133,16 +212,34 @@ export const SchedulerRow = ({ groups, indexRow, cellTop, cellWidth, cellHeight
|
|||||||
onClose={handlePopoverClose}
|
onClose={handlePopoverClose}
|
||||||
disableRestoreFocus
|
disableRestoreFocus
|
||||||
>
|
>
|
||||||
<Typography>
|
<div
|
||||||
<p>{groups[index].name}</p>
|
style={{ display: 'flex', flexDirection: 'column', zIndex: 20000 }}
|
||||||
<p>{groups[index].lecturer}</p>
|
onClick={() => {
|
||||||
<p>{groups[index].room}</p>
|
console.log('XDD');
|
||||||
</Typography>
|
}}
|
||||||
|
>
|
||||||
|
<p style={{ margin: '7px 0 7px 0', fontWeight: 'bold' }}>{groups[index].name}</p>
|
||||||
|
<p style={{ margin: '2px 0 2px 0' }}>
|
||||||
|
<PopoverSpan>Prowadzący:</PopoverSpan> {groups[index].lecturer}
|
||||||
|
</p>
|
||||||
|
<p style={{ margin: '2px 0 2px 0' }}>
|
||||||
|
<PopoverSpan>Sala zajęć</PopoverSpan>: {groups[index].room}
|
||||||
|
</p>
|
||||||
|
<p style={{ margin: '2px 0 2px 0' }}>
|
||||||
|
<PopoverSpan>Kod przedmiotu: </PopoverSpan>ACB129
|
||||||
|
</p>
|
||||||
|
<p style={{ margin: '2px 0 2px 0' }}>
|
||||||
|
<PopoverSpan>Kod grupy: </PopoverSpan>FVJ753
|
||||||
|
</p>
|
||||||
|
<p style={{ margin: '2px 0 2px 0' }}>
|
||||||
|
<PopoverSpan>Punkty ECTS:</PopoverSpan> 2
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
</>
|
</Fragment>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
</ClassesWrapper>
|
</SchedulerEventsWrapper>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
109
src/components/SelectMenu.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import { ClickAwayListener } from '@material-ui/core';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { ExpandIcon } from './CourseCard';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
min-width:130px;
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
background-color: #f1f2f5;
|
||||||
|
color: black;
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
border-bottom-left-radius: 6px;
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
padding-left: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
border-bottom-left-radius: 6px;
|
||||||
|
user-select: none;
|
||||||
|
`;
|
||||||
|
const Header = styled.div`
|
||||||
|
display: flex;
|
||||||
|
width:100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
`;
|
||||||
|
const HeaderTitle = styled.div`
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
`;
|
||||||
|
const List = styled.ul`
|
||||||
|
position: absolute;
|
||||||
|
top: 50px;
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: -15px;
|
||||||
|
background-color: #f2f4f7;
|
||||||
|
`;
|
||||||
|
const ListItem = styled.li`
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
user-select: none;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 37px;
|
||||||
|
font-weight: 400;
|
||||||
|
:hover {
|
||||||
|
background-color: #eceef4;
|
||||||
|
}
|
||||||
|
:first-child{
|
||||||
|
margin-top:10px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const ExpandIconSelect = styled(ExpandIcon)`
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
`;
|
||||||
|
export const SelectMenu = () => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [selectedOption, setSelectedOption] = useState('przedmioty');
|
||||||
|
return (
|
||||||
|
<ClickAwayListener
|
||||||
|
onClickAway={() => {
|
||||||
|
setIsOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Wrapper>
|
||||||
|
<Header
|
||||||
|
onClick={() => {
|
||||||
|
console.log('clicked');
|
||||||
|
setIsOpen(!isOpen);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HeaderTitle>{selectedOption}</HeaderTitle>
|
||||||
|
<ExpandIconSelect selected={isOpen} />
|
||||||
|
</Header>
|
||||||
|
{isOpen && (
|
||||||
|
<List>
|
||||||
|
<ListItem
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedOption('przedmioty');
|
||||||
|
setIsOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
przedmioty
|
||||||
|
</ListItem>
|
||||||
|
<ListItem
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedOption('studenci');
|
||||||
|
setIsOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
studenci
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
)}
|
||||||
|
</Wrapper>
|
||||||
|
</ClickAwayListener>
|
||||||
|
);
|
||||||
|
};
|
@ -1,6 +1,4 @@
|
|||||||
import React, { useState, MouseEvent, ChangeEvent, useEffect } from 'react';
|
import React, { useState, MouseEvent, ChangeEvent, useEffect, useCallback } from 'react';
|
||||||
import Transfer from '../assets/transfer.png';
|
|
||||||
import Search from '../assets/search.svg';
|
|
||||||
import { ReactComponent as Close } from '../assets/close.svg';
|
import { ReactComponent as Close } from '../assets/close.svg';
|
||||||
import ProfileIcon from '../assets/account.svg';
|
import ProfileIcon from '../assets/account.svg';
|
||||||
import { Profile } from './Profile';
|
import { Profile } from './Profile';
|
||||||
@ -9,9 +7,10 @@ import PolishIcon from '../assets/poland.svg';
|
|||||||
import EnglishIcon from '../assets/united-kingdom.svg';
|
import EnglishIcon from '../assets/united-kingdom.svg';
|
||||||
import styled from 'styled-components/macro';
|
import styled from 'styled-components/macro';
|
||||||
import ClickAwayListener from 'react-click-away-listener';
|
import ClickAwayListener from 'react-click-away-listener';
|
||||||
|
import { SelectMenu } from './SelectMenu';
|
||||||
|
|
||||||
const Topbar = styled.div`
|
const Topbar = styled.div`
|
||||||
background-color: #E3E5ED;
|
background-color: #e3e5ed;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
@ -31,19 +30,22 @@ const LogoWrapper = styled.div`
|
|||||||
const Logo = styled.img`
|
const Logo = styled.img`
|
||||||
width: 70px;
|
width: 70px;
|
||||||
height: 70px;
|
height: 70px;
|
||||||
@media only screen and (max-width: 670px) {
|
@media only screen and (max-width: 1533px) {
|
||||||
width: 60px;
|
flex: auto;
|
||||||
height: 60px;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Text = styled.div`
|
const Text = styled.div`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@media only screen and (max-width: 670px) {
|
@media only screen and (max-width: 1533px) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@media only screen and (max-width: 1828px) {
|
||||||
|
margin-right: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FlexboxColumn = styled.div`
|
const FlexboxColumn = styled.div`
|
||||||
@ -53,28 +55,37 @@ const FlexboxColumn = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const InputWrapper = styled.div`
|
const InputWrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
|
max-height: 40px;
|
||||||
background-color: #f2f4f7;
|
background-color: #f2f4f7;
|
||||||
border-radius: 6px;
|
border-radius: 6px 6px 6px 6px;
|
||||||
align-items: center;
|
padding-left: 6px;
|
||||||
|
&:hover {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
&:hover > input {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Input = styled.input`
|
const Input = styled.input`
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 18px;
|
||||||
background-color: #f1f2f5;
|
background-color: #f1f2f5;
|
||||||
font-size: 20px;
|
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
max-height: 40px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
border-top-left-radius: 6px;
|
|
||||||
border-bottom-left-radius: 6px;
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const CloseIcon = styled(Close)`
|
const CloseIcon = styled(Close)`
|
||||||
|
align-self: center;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
@ -103,9 +114,9 @@ const Icon = styled.img`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const Flexbox = styled.div`
|
||||||
|
display: flex;
|
||||||
|
`;
|
||||||
|
|
||||||
interface TopbarProps {
|
interface TopbarProps {
|
||||||
handleTransfer: (e: MouseEvent) => void;
|
handleTransfer: (e: MouseEvent) => void;
|
||||||
@ -122,21 +133,22 @@ export default function ({ handleTransfer }: TopbarProps) {
|
|||||||
|
|
||||||
const handleProfile = (event: MouseEvent<HTMLImageElement>) => setAnchorEl(event.currentTarget);
|
const handleProfile = (event: MouseEvent<HTMLImageElement>) => setAnchorEl(event.currentTarget);
|
||||||
|
|
||||||
const handleClose = () => setAnchorEl(null);
|
const handleCloseProfile = () => setAnchorEl(null);
|
||||||
|
|
||||||
const handleClearInput = () => setClearInput(!clearInput);
|
const handleClearInput = useCallback(() => setClearInput((clearInput) => !clearInput), []);
|
||||||
|
|
||||||
const handleChange = (event: ChangeEvent<HTMLInputElement>) => setInput(event.target.value);
|
const handleChange = (event: ChangeEvent<HTMLInputElement>) => setInput(event.target.value);
|
||||||
|
|
||||||
const handleClick = () => setOpen(true);
|
const handleShowDropdown = () => setOpen(true);
|
||||||
|
|
||||||
const handleCloseDropdown = () => setOpen(false);
|
const handleCloseDropdown = () => setOpen(false);
|
||||||
|
|
||||||
const handleClickAway = () => setOpen(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
clearInput && (setInput(''), handleClearInput());
|
if (clearInput) {
|
||||||
}, [clearInput]);
|
setInput('');
|
||||||
|
handleClearInput();
|
||||||
|
}
|
||||||
|
}, [clearInput, handleClearInput]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Topbar>
|
<Topbar>
|
||||||
@ -145,11 +157,26 @@ export default function ({ handleTransfer }: TopbarProps) {
|
|||||||
<Text> plan na plan </Text>
|
<Text> plan na plan </Text>
|
||||||
</LogoWrapper>
|
</LogoWrapper>
|
||||||
<FlexboxColumn>
|
<FlexboxColumn>
|
||||||
<ClickAwayListener onClickAway={handleClickAway}>
|
<ClickAwayListener onClickAway={handleCloseDropdown}>
|
||||||
<InputWrapper>
|
<Flexbox>
|
||||||
<Input placeholder="Wyszukaj przedmiot..." onChange={handleChange} onClick={handleClick} value={input} />
|
{/* <SelectMenu /> */}
|
||||||
<CloseIcon onClick={handleClearInput} />
|
|
||||||
</InputWrapper>
|
<InputWrapper>
|
||||||
|
{/* <SelectSearch value={value} onChange={Change}>
|
||||||
|
<SelectOption value="przedmiot">Przedmiot</SelectOption>
|
||||||
|
<SelectOption value="student">Student</SelectOption>
|
||||||
|
</SelectSearch> */}
|
||||||
|
<Input
|
||||||
|
placeholder={`Wyszukaj...`}
|
||||||
|
onChange={handleChange}
|
||||||
|
value={input}
|
||||||
|
onFocus={() => {
|
||||||
|
handleShowDropdown();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<CloseIcon onClick={handleClearInput} />
|
||||||
|
</InputWrapper>
|
||||||
|
</Flexbox>
|
||||||
<Dropdown open={open} input={input} handleCloseDropdown={handleCloseDropdown} />
|
<Dropdown open={open} input={input} handleCloseDropdown={handleCloseDropdown} />
|
||||||
</ClickAwayListener>
|
</ClickAwayListener>
|
||||||
</FlexboxColumn>
|
</FlexboxColumn>
|
||||||
@ -159,7 +186,7 @@ export default function ({ handleTransfer }: TopbarProps) {
|
|||||||
{/* <Icon alt="transfer" src={Transfer} onClick={handleTransfer} /> */}
|
{/* <Icon alt="transfer" src={Transfer} onClick={handleTransfer} /> */}
|
||||||
<Icon alt="change_language" src={isPolish ? EnglishIcon : PolishIcon} onClick={onLangChange} />
|
<Icon alt="change_language" src={isPolish ? EnglishIcon : PolishIcon} onClick={onLangChange} />
|
||||||
<Icon alt="profile" src={ProfileIcon} onClick={handleProfile} />
|
<Icon alt="profile" src={ProfileIcon} onClick={handleProfile} />
|
||||||
<Profile anchorEl={anchorEl} handleClose={handleClose} />
|
<Profile anchorEl={anchorEl} handleClose={handleCloseProfile} />
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
</Topbar>
|
</Topbar>
|
||||||
);
|
);
|
||||||
|
@ -1,37 +1,44 @@
|
|||||||
export const days = [
|
export const days = ['', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek'];
|
||||||
"",
|
|
||||||
"Poniedziałek",
|
|
||||||
"Wtorek",
|
|
||||||
"Środa",
|
|
||||||
"Czwartek",
|
|
||||||
"Piątek",
|
|
||||||
];
|
|
||||||
export const hours = [
|
export const hours = [
|
||||||
"8:00",
|
'8:00',
|
||||||
"",
|
'',
|
||||||
"9:00",
|
'9:00',
|
||||||
"",
|
'',
|
||||||
"10:00",
|
'10:00',
|
||||||
"",
|
'',
|
||||||
"11:00",
|
'11:00',
|
||||||
"",
|
'',
|
||||||
"12:00",
|
'12:00',
|
||||||
"",
|
'',
|
||||||
"13:00",
|
'13:00',
|
||||||
"",
|
'',
|
||||||
"14:00",
|
'14:00',
|
||||||
"",
|
'',
|
||||||
"15:00",
|
'15:00',
|
||||||
"",
|
'',
|
||||||
"16:00",
|
'16:00',
|
||||||
"",
|
'',
|
||||||
"17:00",
|
'17:00',
|
||||||
"",
|
'',
|
||||||
"18:00",
|
'18:00',
|
||||||
"",
|
'',
|
||||||
"19:00",
|
'19:00',
|
||||||
"",
|
'',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
export const MONDAY_TO_FRIDAY = 5;
|
export const MONDAY_TO_FRIDAY = 5;
|
||||||
|
|
||||||
|
//added 12:00, one of lectures starts at that time
|
||||||
|
export const courseStartTimeToEventRow: { [time: string]: number } = {
|
||||||
|
'8.15': 0,
|
||||||
|
'10.00': 1,
|
||||||
|
'11.45': 2,
|
||||||
|
'12.00': 2,
|
||||||
|
'13.45': 3,
|
||||||
|
'15.30': 4,
|
||||||
|
'17.15': 5,
|
||||||
|
'18.45': 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
//groupTimeToEventRowMapping - 1;
|
||||||
|
export const ROWS_COUNT = 6;
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React, { useState, useEffect, createContext, ReactNode } from 'react';
|
import React, { useState, useEffect, createContext, ReactNode } from 'react';
|
||||||
import { User } from '../types';
|
import { User } from '../types';
|
||||||
import axios from 'axios';
|
import { axiosInstance } from '../utils/axiosInstance';
|
||||||
|
|
||||||
export interface CASContext {
|
export interface CASContext {
|
||||||
user?: User;
|
user?: User;
|
||||||
logout: () => void;
|
logout: () => void;
|
||||||
|
token?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CASContext = createContext<CASContext | undefined>(undefined);
|
export const CASContext = createContext<CASContext | undefined>(undefined);
|
||||||
@ -15,29 +16,28 @@ export interface CASProviderProps {
|
|||||||
|
|
||||||
export const CASProvider = ({ children }: CASProviderProps) => {
|
export const CASProvider = ({ children }: CASProviderProps) => {
|
||||||
const [user, setUser] = useState<User | undefined>(undefined);
|
const [user, setUser] = useState<User | undefined>(undefined);
|
||||||
|
const [token, setToken] = useState<string | null>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const login = async () => {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const ticket = urlParams.get('ticket');
|
||||||
|
if (!ticket) {
|
||||||
|
redirectToCASLoginService();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (!sessionStorage.getItem('userToken')) {
|
||||||
|
const { data: token } = await axiosInstance.get(`${process.env.REACT_APP_API_URL}/token?ticket=${ticket}`);
|
||||||
|
sessionStorage.setItem('userToken', token);
|
||||||
|
}
|
||||||
|
const token = sessionStorage.getItem('userToken');
|
||||||
|
setToken(token);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
login();
|
login();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const login = async () => {
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const ticket = urlParams.get('ticket');
|
|
||||||
if (!ticket) {
|
|
||||||
redirectToCASLoginService();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (!sessionStorage.getItem('userToken')) {
|
|
||||||
const { data: token } = await axios.get(`${process.env.REACT_APP_API_URL}/token?ticket=${ticket}`);
|
|
||||||
sessionStorage.setItem('userToken', token);
|
|
||||||
setUser({ ...user, token });
|
|
||||||
}
|
|
||||||
const token = sessionStorage.getItem('userToken');
|
|
||||||
setUser({ ...user, token });
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
redirectToCASLogoutService();
|
redirectToCASLogoutService();
|
||||||
}
|
}
|
||||||
@ -50,5 +50,5 @@ export const CASProvider = ({ children }: CASProviderProps) => {
|
|||||||
window.location.replace(`https://cas.amu.edu.pl/cas/login?service=${window.origin}&locale=pl`);
|
window.location.replace(`https://cas.amu.edu.pl/cas/login?service=${window.origin}&locale=pl`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <CASContext.Provider value={{ user, logout }}>{children}</CASContext.Provider>;
|
return <CASContext.Provider value={{ user, token, logout }}>{children}</CASContext.Provider>;
|
||||||
};
|
};
|
||||||
|
@ -1,16 +1,33 @@
|
|||||||
import React, { useState, createContext, useEffect, ReactNode, useContext } from 'react';
|
import React, { useState, createContext, useEffect, ReactNode } from 'react';
|
||||||
import { Course, Group, Basket, GroupType } from '../types';
|
import { Course, Group, Basket, GroupType, SchedulerEvent } from '../types';
|
||||||
import axios from 'axios';
|
|
||||||
import { CASContext } from './CASProvider';
|
|
||||||
import { useSnackbar } from 'notistack';
|
import { useSnackbar } from 'notistack';
|
||||||
|
import { createClassTime } from '../utils';
|
||||||
|
import { axiosInstance } from '../utils/axiosInstance';
|
||||||
|
import CloseIcon from '@material-ui/icons/Close';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const StyledCloseIcon = styled(CloseIcon)`
|
||||||
|
color: #000000;
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
interface CourseContext {
|
interface CourseContext {
|
||||||
courses: Array<Course>;
|
courses: Array<Course>;
|
||||||
basket: Array<Basket>;
|
basket: Array<Basket>;
|
||||||
addToBasket: (courses: Course) => void;
|
hoveredGroup: Group | undefined | null;
|
||||||
addGroup: (group: Group, id: number) => void;
|
addCourseToBasket: (courses: Course) => void;
|
||||||
|
changeHoveredGroup: (group: Group | null) => void;
|
||||||
|
changeGroupInBasket: (group: Group, courseId: number) => void;
|
||||||
|
restoreGroupInBasket: (restoreGroup: Group, courseId: number) => void;
|
||||||
deleteFromBasket: (id: number) => void;
|
deleteFromBasket: (id: number) => void;
|
||||||
saveBasket: () => void;
|
saveBasket: () => void;
|
||||||
|
selectSchedulerEvents: () => Array<SchedulerEvent>;
|
||||||
|
selectBasketNames: () => Array<string>;
|
||||||
|
selectBasketCourses: () => Array<Course>;
|
||||||
|
selectBasketCourseGroups: (courseId: number) => { lecture: Group | undefined; classes: Group | undefined };
|
||||||
}
|
}
|
||||||
export const coursesContext = createContext<CourseContext | undefined>(undefined);
|
export const coursesContext = createContext<CourseContext | undefined>(undefined);
|
||||||
|
|
||||||
@ -19,28 +36,60 @@ interface CoursesProviderProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const CoursesProvider = ({ children }: CoursesProviderProps) => {
|
export const CoursesProvider = ({ children }: CoursesProviderProps) => {
|
||||||
//fetch courses with groups
|
|
||||||
const [courses, setCourses] = useState<Array<Course>>([]);
|
|
||||||
const [basket, setBasket] = useState<Array<Basket>>([]);
|
|
||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
const { closeSnackbar } = useSnackbar();
|
const { closeSnackbar } = useSnackbar();
|
||||||
|
|
||||||
const CAS = useContext(CASContext)!;
|
//fetch courses with groups
|
||||||
const token = CAS?.user?.token;
|
const [courses, setCourses] = useState<Array<Course>>([]);
|
||||||
|
const [basket, setBasket] = useState<Array<Basket>>([]);
|
||||||
const selectBasketIds = (basket: Array<Basket>) => {
|
const [hoveredGroup, setHoveredGroup] = useState<Group | undefined | null>(null);
|
||||||
const classesIds = basket.map((course) => course.classes.id);
|
const selectBasketIds = () => {
|
||||||
const lecturesIds = basket.map((course) => course?.lecture?.id);
|
const classesIds = basket.map((course) => course?.classes?.id).filter((course) => course !== undefined);
|
||||||
|
const lecturesIds = basket.map((course) => course?.lecture?.id).filter((course) => course !== undefined);
|
||||||
return lecturesIds[0] === undefined ? classesIds : [...classesIds, ...lecturesIds];
|
return [...classesIds, ...lecturesIds];
|
||||||
};
|
};
|
||||||
|
|
||||||
const addToBasket = (course: Course) => {
|
const selectBasketNames = () => basket.map(({ name }) => name);
|
||||||
|
|
||||||
|
const selectBasketCourses = () => {
|
||||||
|
const basketNames = selectBasketNames();
|
||||||
|
return basketNames.reduce((sum, basketName) => {
|
||||||
|
const course = courses.find(({ name }) => basketName === name);
|
||||||
|
return course === undefined ? sum : [...sum, course];
|
||||||
|
}, [] as Array<Course>);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectSchedulerEvents = () => {
|
||||||
|
return basket.reduce((res, el) => {
|
||||||
|
const { name } = el;
|
||||||
|
if (el.classes) {
|
||||||
|
const { time } = el.classes;
|
||||||
|
res.push({ ...el.classes, name, time: createClassTime(time) });
|
||||||
|
}
|
||||||
|
if (el.lecture) {
|
||||||
|
const { time } = el.lecture;
|
||||||
|
res.push({ ...el.lecture, name, time: createClassTime(time) });
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}, [] as Array<SchedulerEvent>);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectBasketCourseGroups = (courseId: number) => {
|
||||||
|
const course = basket.find(({ id }) => id === courseId);
|
||||||
|
if (course !== undefined) {
|
||||||
|
return { lecture: course.lecture, classes: course.classes };
|
||||||
|
} else {
|
||||||
|
return { lecture: undefined, classes: undefined };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeHoveredGroup = (group: Group | null) => setHoveredGroup(group);
|
||||||
|
|
||||||
|
const addCourseToBasket = (course: Course) => {
|
||||||
const courseToBasket: Basket = {
|
const courseToBasket: Basket = {
|
||||||
name: course.name,
|
name: course.name,
|
||||||
id: course.id,
|
id: course.id,
|
||||||
classes: course.classes[0],
|
classes: course.classes !== undefined ? course.classes[0] : undefined,
|
||||||
lecture: course.lectures !== undefined ? course.lectures[0] : undefined,
|
lecture: course.lectures !== undefined ? course.lectures[0] : undefined,
|
||||||
};
|
};
|
||||||
setBasket([...basket, courseToBasket]);
|
setBasket([...basket, courseToBasket]);
|
||||||
@ -49,37 +98,25 @@ export const CoursesProvider = ({ children }: CoursesProviderProps) => {
|
|||||||
const deleteFromBasket = (id: number) => setBasket(basket.filter((course) => course.id !== id));
|
const deleteFromBasket = (id: number) => setBasket(basket.filter((course) => course.id !== id));
|
||||||
|
|
||||||
const saveBasket = async () => {
|
const saveBasket = async () => {
|
||||||
const basketIds = selectBasketIds(basket);
|
const basketIds = selectBasketIds();
|
||||||
|
|
||||||
const config = {
|
|
||||||
method: 'post' as const,
|
|
||||||
url: `${process.env.REACT_APP_API_URL}/api/v1/commisions/add?`,
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
data: JSON.stringify(basketIds),
|
|
||||||
};
|
|
||||||
|
|
||||||
const action = (key: any) => (
|
const action = (key: any) => (
|
||||||
<>
|
<>
|
||||||
<button
|
<StyledCloseIcon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
closeSnackbar(key);
|
closeSnackbar(key);
|
||||||
}}
|
}}
|
||||||
>
|
></StyledCloseIcon>
|
||||||
X
|
|
||||||
</button>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await axios.request(config);
|
await axiosInstance.post(`${process.env.REACT_APP_API_URL}/api/v1/commisions/user`, JSON.stringify(basketIds));
|
||||||
enqueueSnackbar('Plan został zapisany', {
|
enqueueSnackbar('Plan został zapisany', {
|
||||||
variant: 'success',
|
variant: 'success',
|
||||||
action,
|
action,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.log('error: ', e);
|
||||||
enqueueSnackbar('Zapisywanie planu nie powiodło się', {
|
enqueueSnackbar('Zapisywanie planu nie powiodło się', {
|
||||||
variant: 'error',
|
variant: 'error',
|
||||||
action,
|
action,
|
||||||
@ -87,8 +124,8 @@ export const CoursesProvider = ({ children }: CoursesProviderProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addGroup = (choosenGroup: Group, id: number) => {
|
const changeGroupInBasket = (choosenGroup: Group, courseId: number) => {
|
||||||
const basketCourse = basket.filter((course) => course.id === id)[0];
|
const basketCourse = basket.filter((course) => course.id === courseId)[0];
|
||||||
const { type } = choosenGroup;
|
const { type } = choosenGroup;
|
||||||
if (type === GroupType.CLASS) {
|
if (type === GroupType.CLASS) {
|
||||||
setBasket(
|
setBasket(
|
||||||
@ -99,48 +136,72 @@ export const CoursesProvider = ({ children }: CoursesProviderProps) => {
|
|||||||
basket.map((basket) => (basket.id === basketCourse.id ? { ...basket, lecture: choosenGroup } : basket)),
|
basket.map((basket) => (basket.id === basketCourse.id ? { ...basket, lecture: choosenGroup } : basket)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
changeHoveredGroup(choosenGroup);
|
||||||
|
};
|
||||||
|
|
||||||
|
const restoreGroupInBasket = (restoreGroup: Group, courseId: number) => {
|
||||||
|
const basketCourse = basket.filter((course) => course.id === courseId)[0];
|
||||||
|
const { type } = restoreGroup;
|
||||||
|
if (type === GroupType.CLASS) {
|
||||||
|
setBasket(
|
||||||
|
basket.map((basket) => (basket.id === basketCourse.id ? { ...basket, classes: restoreGroup } : basket)),
|
||||||
|
);
|
||||||
|
} else if (type === GroupType.LECTURE) {
|
||||||
|
setBasket(
|
||||||
|
basket.map((basket) => (basket.id === basketCourse.id ? { ...basket, lecture: restoreGroup } : basket)),
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNewestTimetable = async () => {
|
const getNewestTimetable = async () => {
|
||||||
const config = {
|
|
||||||
method: 'get' as const,
|
|
||||||
url: `${process.env.REACT_APP_API_URL}/api/v1/assignments/getCurrentAssignments`,
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
try {
|
try {
|
||||||
let { data: basket } = await axios.request(config);
|
const { data } = await axiosInstance.get(
|
||||||
if (basket === '') {
|
`${process.env.REACT_APP_API_URL}/api/v1/assignments/user`,
|
||||||
basket = [];
|
);
|
||||||
}
|
const basket = data === '' ? [] : data;
|
||||||
setBasket(basket);
|
setBasket(basket);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchClasses = async () => {
|
const fetchCourses = async () => {
|
||||||
try {
|
try {
|
||||||
const { data: courses } = await axios.get<Array<Course>>(
|
const { data: courses } = await axiosInstance.get<Array<Course>>(
|
||||||
`${process.env.REACT_APP_API_URL}/api/v1/courses/getCoursesWithGroups`,
|
`${process.env.REACT_APP_API_URL}/api/v1/courses/all?groups=true`,
|
||||||
);
|
);
|
||||||
courses.sort((a, b) => (a.name > b.name ? 1 : -1));
|
const sortedCourses = courses.sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||||
setCourses(courses);
|
setCourses(sortedCourses);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchClasses();
|
setTimeout(() => {
|
||||||
if (token) {
|
fetchCourses();
|
||||||
getNewestTimetable();
|
getNewestTimetable();
|
||||||
}
|
}, 200);
|
||||||
}, [token]);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<coursesContext.Provider value={{ courses, basket, addToBasket, addGroup, deleteFromBasket, saveBasket }}>
|
<coursesContext.Provider
|
||||||
|
value={{
|
||||||
|
courses,
|
||||||
|
basket,
|
||||||
|
hoveredGroup,
|
||||||
|
addCourseToBasket,
|
||||||
|
changeHoveredGroup,
|
||||||
|
changeGroupInBasket,
|
||||||
|
deleteFromBasket,
|
||||||
|
restoreGroupInBasket,
|
||||||
|
saveBasket,
|
||||||
|
selectSchedulerEvents,
|
||||||
|
selectBasketNames,
|
||||||
|
selectBasketCourses,
|
||||||
|
selectBasketCourseGroups,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</coursesContext.Provider>
|
</coursesContext.Provider>
|
||||||
);
|
);
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
// import { Group, Course } from '../types';
|
|
||||||
|
|
||||||
export enum Types {
|
|
||||||
addToBasket = 'ADD_CHOOSEN_COURSE',
|
|
||||||
removeChoosenCourse = 'REMOVE_CHOOSEN_COURSE',
|
|
||||||
addGroup = 'ADD_CHOOSEN_GROUP',
|
|
||||||
removeChoosenGroup = 'REMOVE_CHOOSEN_GROUP',
|
|
||||||
}
|
|
||||||
|
|
||||||
// type ChoosenCoursesPayload = {
|
|
||||||
// [Types.addToBasket]: {};
|
|
||||||
// };
|
|
||||||
|
|
||||||
// type ChoosenGroupsPayload = {
|
|
||||||
// [Types.Create]: {
|
|
||||||
// id: number;
|
|
||||||
// name: string;
|
|
||||||
// price: number;
|
|
||||||
// };
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export const choosenGroupsReducer = (state, action) => {
|
|
||||||
// switch (action.type) {
|
|
||||||
// case Types.addGroup:
|
|
||||||
// return add;
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
//https://dev.to/elisealcala/react-context-with-usereducer-and-typescript-4obm
|
|
@ -7,7 +7,7 @@ export interface Basket {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
lecture?: Group;
|
lecture?: Group;
|
||||||
classes: Group;
|
classes?: Group;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Group {
|
export interface Group {
|
||||||
@ -24,11 +24,21 @@ export interface Course {
|
|||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
lectures?: Array<Group>;
|
lectures?: Array<Group>;
|
||||||
classes: Array<Group>;
|
classes?: Array<Group>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
name?: string;
|
name?: string;
|
||||||
surname?: string;
|
surname?: string;
|
||||||
token: string | null;
|
}
|
||||||
|
|
||||||
|
export interface SchedulerEvent {
|
||||||
|
id: number;
|
||||||
|
day: number;
|
||||||
|
time: [string, string];
|
||||||
|
lecturer: string;
|
||||||
|
room: string;
|
||||||
|
type: GroupType;
|
||||||
|
capacity?: number;
|
||||||
|
name: string;
|
||||||
}
|
}
|
||||||
|
15
src/utils/axiosInstance.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export const axiosInstance = axios.create();
|
||||||
|
|
||||||
|
axiosInstance.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
const token = sessionStorage.getItem('userToken');
|
||||||
|
config.headers['Authorization'] = 'Bearer ' + token;
|
||||||
|
config.headers['Content-Type'] = 'application/json';
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
Promise.reject(error);
|
||||||
|
},
|
||||||
|
);
|
80
src/utils/index.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { courseStartTimeToEventRow } from '../constants/index';
|
||||||
|
import { SchedulerEvent } from '../types';
|
||||||
|
|
||||||
|
export const createClassTime = (startTime: string): [string, string] => {
|
||||||
|
const startTimeMapped = courseStartTimeToEventRow[startTime];
|
||||||
|
const endTime = Object.keys(courseStartTimeToEventRow).find(
|
||||||
|
(key) => courseStartTimeToEventRow[key] === startTimeMapped + 1,
|
||||||
|
)!;
|
||||||
|
return [startTime, endTime];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectGroupsToShow = (schedulerEvents: Array<SchedulerEvent>, index: number) => {
|
||||||
|
return schedulerEvents.filter(({ time }: { time: [string, string] }) => courseStartTimeToEventRow[time[0]] === index);
|
||||||
|
};
|
||||||
|
|
||||||
|
type Procedure = (...args: any[]) => any;
|
||||||
|
|
||||||
|
interface Debounce {
|
||||||
|
(...args: any[]): any;
|
||||||
|
clear: () => void;
|
||||||
|
flush: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const debounce = (func: Procedure, wait: number, immediate: boolean = false) => {
|
||||||
|
let timeout: number | null;
|
||||||
|
let args: any;
|
||||||
|
let context: any;
|
||||||
|
let result: any;
|
||||||
|
|
||||||
|
const later = () => {
|
||||||
|
timeout = null;
|
||||||
|
if (!immediate) {
|
||||||
|
result = func.apply(context, args);
|
||||||
|
context = args = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedFunc: Procedure = function (this: any) {
|
||||||
|
context = this;
|
||||||
|
args = arguments;
|
||||||
|
const callNow = immediate && !timeout;
|
||||||
|
|
||||||
|
if (!timeout) {
|
||||||
|
timeout = window.setTimeout(later, wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callNow) {
|
||||||
|
result = func.apply(context, args);
|
||||||
|
context = args = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
if (timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const flush = () => {
|
||||||
|
if (timeout) {
|
||||||
|
result = func.apply(context, args);
|
||||||
|
context = args = null;
|
||||||
|
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const debounced: Debounce = (() => {
|
||||||
|
const f: any = debouncedFunc;
|
||||||
|
f.clear = clear;
|
||||||
|
f.flush = flush;
|
||||||
|
return f;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return debounced;
|
||||||
|
};
|