diff --git a/package-lock.json b/package-lock.json index 892300e..4cfca3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,11 +12,14 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "bootstrap": "^5.3.2", + "http-proxy-middleware": "^2.0.6", "react": "^18.2.0", "react-bootstrap": "^2.10.1", "react-bootstrap-icons": "^1.10.3", "react-dom": "^18.2.0", - "react-router-dom": "^6.22.0", + "react-icons": "^5.0.1", + "react-loader-spinner": "^6.1.6", + "react-router-dom": "^6.22.1", "web-vitals": "^2.1.4" }, "devDependencies": { @@ -2442,6 +2445,24 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -3620,9 +3641,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.0.tgz", - "integrity": "sha512-HOil5aFtme37dVQTB6M34G95kPM3MMuqSmIRVCC52eKV+Y/tGSqw9P3rWhlAx6A+mz+MoX+XxsGsNJbaI5qCgQ==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.1.tgz", + "integrity": "sha512-zcU0gM3z+3iqj8UX45AmWY810l3oUmXM7uH4dt5xtzvMhRtYVhKGOmgOd1877dOPPepfCjUv57w+syamWIYe7w==", "engines": { "node": ">=14.0.0" } @@ -4386,7 +4407,7 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, + "devOptional": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -4405,7 +4426,7 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, + "devOptional": true, "dependencies": { "@types/node": "*" } @@ -4450,7 +4471,7 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, + "devOptional": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -4462,7 +4483,7 @@ "version": "4.17.43", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", - "dev": true, + "devOptional": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -4489,13 +4510,12 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true + "devOptional": true }, "node_modules/@types/http-proxy": { "version": "1.17.14", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -4773,7 +4793,7 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true + "devOptional": true }, "node_modules/@types/node": { "version": "20.11.17", @@ -4819,13 +4839,13 @@ "version": "6.9.11", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", - "dev": true + "devOptional": true }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true + "devOptional": true }, "node_modules/@types/react": { "version": "18.2.55", @@ -4883,7 +4903,7 @@ "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, + "devOptional": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -4902,7 +4922,7 @@ "version": "1.15.5", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@types/http-errors": "*", "@types/mime": "*", @@ -4923,6 +4943,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "node_modules/@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -6510,6 +6535,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6990,6 +7023,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -7192,6 +7233,16 @@ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "dev": true }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -8875,8 +8926,7 @@ "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "node_modules/events": { "version": "3.3.0", @@ -9239,7 +9289,6 @@ "version": "1.15.5", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", - "dev": true, "funding": [ { "type": "individual", @@ -10104,7 +10153,6 @@ "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -10132,7 +10180,6 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dev": true, "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", @@ -10493,7 +10540,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -10547,7 +10593,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -10625,7 +10670,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true, "engines": { "node": ">=10" }, @@ -13786,7 +13830,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -14360,8 +14403,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -15797,8 +15839,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -16284,6 +16325,14 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", "dev": true }, + "node_modules/react-icons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz", + "integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -16294,6 +16343,27 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, + "node_modules/react-loader-spinner": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-6.1.6.tgz", + "integrity": "sha512-x5h1Jcit7Qn03MuKlrWcMG9o12cp9SNDVHVJTNRi9TgtGPKcjKiXkou4NRfLAtXaFB3+Z8yZsVzONmPzhv2ErA==", + "dependencies": { + "react-is": "^18.2.0", + "styled-components": "^6.1.2" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-loader-spinner/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -16304,11 +16374,11 @@ } }, "node_modules/react-router": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz", - "integrity": "sha512-q2yemJeg6gw/YixRlRnVx6IRJWZD6fonnfZhN1JIOhV2iJCPeRNSH3V1ISwHf+JWcESzLC3BOLD1T07tmO5dmg==", + "version": "6.22.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.1.tgz", + "integrity": "sha512-0pdoRGwLtemnJqn1K0XHUbnKiX0S4X8CgvVVmHGOWmofESj31msHo/1YiqcJWK7Wxfq2a4uvvtS01KAQyWK/CQ==", "dependencies": { - "@remix-run/router": "1.15.0" + "@remix-run/router": "1.15.1" }, "engines": { "node": ">=14.0.0" @@ -16318,12 +16388,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz", - "integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==", + "version": "6.22.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.1.tgz", + "integrity": "sha512-iwMyyyrbL7zkKY7MRjOVRy+TMnS/OPusaFVxM2P11x9dzSzGmLsebkCvYirGq0DWB9K9hOspHYYtDz33gE5Duw==", "dependencies": { - "@remix-run/router": "1.15.0", - "react-router": "6.22.0" + "@remix-run/router": "1.15.1", + "react-router": "6.22.1" }, "engines": { "node": ">=14.0.0" @@ -16637,8 +16707,7 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "node_modules/resolve": { "version": "1.22.8", @@ -17268,6 +17337,11 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -17365,7 +17439,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -17845,6 +17918,70 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz", + "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.1", + "@emotion/unitless": "0.8.0", + "@types/stylis": "4.2.0", + "css-to-react-native": "3.2.0", + "csstype": "3.1.2", + "postcss": "8.4.31", + "shallowequal": "1.1.0", + "stylis": "4.3.1", + "tslib": "2.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -17861,6 +17998,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", diff --git a/package.json b/package.json index 3ef7268..b91b4e5 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,14 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "bootstrap": "^5.3.2", + "http-proxy-middleware": "^2.0.6", "react": "^18.2.0", "react-bootstrap": "^2.10.1", "react-bootstrap-icons": "^1.10.3", "react-dom": "^18.2.0", - "react-router-dom": "^6.22.0", + "react-icons": "^5.0.1", + "react-loader-spinner": "^6.1.6", + "react-router-dom": "^6.22.1", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/src/components/Requests/requestCard.jsx b/src/components/Requests/requestCard.jsx new file mode 100644 index 0000000..496823f --- /dev/null +++ b/src/components/Requests/requestCard.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import {Card, Container} from 'react-bootstrap'; + +const RequestCard = (props) => { + const {id, dateOfBooking, startTime, endTime, status, ownerRole, classroomNumber } = props; + + let roleType = ""; + switch (ownerRole) { + case 'User': + roleType = 'Пользователь'; + break; + case 'Teacher': + roleType = 'Учитель'; + break; + case 'DeanTeacher': + roleType = 'Учитель-работник деканата'; + break; + case 'Student': + roleType = 'Студент'; + break; + } + + const fullname = "Русик, добавь в апишку имя" + + let backgroundType = ''; + switch (status) { + case 'Pending': + backgroundType = 'secondary-subtle'; + break; + case 'Approved': + backgroundType = 'success-subtle'; + break; + case 'Rejected': + backgroundType = 'danger-subtle'; + break; + } + + + return ( +
+ + + + Заявка на бронирование кабинета №{classroomNumber} + +

Дата бронирования: {dateOfBooking}

+

Время бронирования: {startTime} - {endTime}

+

Автор: {roleType} {fullname}

+

Статус: {status}

+ +
+ +
+
+
+
+ ); +}; + +export default RequestCard; \ No newline at end of file diff --git a/src/components/header.js b/src/components/header.js index 95ed85f..abcf543 100644 --- a/src/components/header.js +++ b/src/components/header.js @@ -1,34 +1,99 @@ -import React from "react"; -import { Container, Row, Col, Button, Navbar, Image,Dropdown,DropdownButton } from 'react-bootstrap'; -import { Link } from "react-router-dom"; +import React, { useState, useEffect } from "react"; +import { Container, Row, Col, Button, Navbar, Image, Dropdown, DropdownButton } from 'react-bootstrap'; +import { Link, useLocation, useNavigate } from "react-router-dom"; +import { LogoutFetch } from "../requests/requestsMetods"; +function Header() { + const location = useLocation(); + const [headerText, setHeaderText] = useState(""); + useEffect(() => { + + switch (location.pathname) { + case "/users": + setHeaderText("Пользователи"); + break; + case "/login": + setHeaderText("Вход"); + break; + case "/keys": + setHeaderText("Ключи"); + break; + case "/requests": + setHeaderText("Заявки"); + break; + + default: + setHeaderText(""); + break; + } + }, [location]); + + const token=localStorage.getItem('token') + const navigate = useNavigate(); + + const handleLogout = async (e) => { + e.preventDefault() + const response=await LogoutFetch(token) + if (response){ + localStorage.clear() + navigate('/login') + } + }; -function Header(){ return (
+ src="https://facultetus.ru/images/universities/fe528b970af8e7a11ee44374ecd12e24.png" + width="35" + height="35" + style={{ filter: 'brightness(0) invert(1)' }} + className="mb-2" + /> TSU.Ключи - - Заявки - Пользователи - Ключи - - Вход + + {/* + {headerText === "Заявки" ? Заявки : "Заявки"} + + + + {headerText === "Пользователи" ? Пользователи : "Пользователи"} - + + {headerText === "Ключи" ? Ключи : "Ключи"} + + */} + { + token ? ( +
+ + {headerText === "Заявки" ? Заявки : "Заявки"} + + + + {headerText === "Пользователи" ? Пользователи : "Пользователи"} + + + {headerText === "Ключи" ? Ключи : "Ключи"} + + + Выход + +
+ // + // Выход + // + ) : + {headerText === "Вход" ? Вход : "Вход"} + + } +
); } -export default Header \ No newline at end of file +export default Header; diff --git a/src/components/login.js b/src/components/login.js index f419a5a..d778fb8 100644 --- a/src/components/login.js +++ b/src/components/login.js @@ -1,23 +1,85 @@ -import React from "react"; -import { Container, Row, Col, Button, Navbar, Image,Dropdown,DropdownButton, Card, CardBody, CardTitle,Form, FormGroup, FormControl, FormLabel } from 'react-bootstrap'; - +import React, { useState } from "react"; +import { Container, Row, Col, Button, Alert,Navbar, Image,Dropdown,DropdownButton, Card, CardBody, CardTitle,Form, FormGroup, FormControl, FormLabel } from 'react-bootstrap'; +import { LoginFetch, ProfileFetch } from "../requests/requestsMetods"; +import { useNavigate } from "react-router-dom"; +import { LineWave as Loader} from "react-loader-spinner"; export default function Login(){ + + const navigate = useNavigate(); + const [data, setData]=useState({ + email: '', + password: '' + }) + const [loading, setLoading] = useState(false); + + const [errorMessage, setErrorMessage] = useState('') + + const handleEmailChange=(e)=>{ + setData({ + ...data, + email: e.target.value + }) + } + const handlePasswordChange=(e)=>{ + setData({ + ...data, + password: e.target.value + }) + } + const handleSubmit=async (e)=>{ + e.preventDefault() + setLoading(true); + + const response=await LoginFetch(data) + + setTimeout(async () => { + setLoading(false); + if (response.token) { + const responseProfile=await ProfileFetch(response.token) + console.log('role',responseProfile.role) + if (responseProfile.role==='DeanTeacher' || responseProfile.role==='Dean' || responseProfile.role==='Administrator'){ + localStorage.setItem('token', response.token) + navigate('/users'); + } + else{ + setErrorMessage('У вас недостаточно прав доступа! Обратитесь к администратору'); + } + + // console.log(responseData); + } + else { + setErrorMessage('Неправильный пароль или email'); + } + }, 1000); + + } + return ( Вход -
+ Email - + - + Пароль - + - + {loading ? ( +
+ +
+ ) :} + + {errorMessage && {errorMessage}}
diff --git a/src/components/userCard.js b/src/components/userCard.js new file mode 100644 index 0000000..6cf3de9 --- /dev/null +++ b/src/components/userCard.js @@ -0,0 +1,80 @@ +import React, {useState} from "react"; +import { Form, ModalHeader,ModalTitle,ModalBody, ModalFooter, Container, Row, Col, Button, Navbar, Image,Modal,CardBody, Card, CardTitle, CardHeader, FormGroup, FormLabel, FormControl, FormSelect } from 'react-bootstrap'; +import { BsFillPersonFill } from "react-icons/bs"; + +export default function UserCard(props){ + const { name, role, email } = props; + + let roleClass; + if(role==='Студент'){ + roleClass='bg-success' + } + if(role==='Преподаватель'){ + roleClass='bg-info' + } + if(role==='Пользователь'){ + roleClass='bg-secondary' + } + const [showModal, setShowModal] = useState(false); + const [selectedRole, setSelectedRole] = useState(role); + + + const handleClose = () => setShowModal(false); + const handleShow = () => setShowModal(true); + + const handleRoleChange = (e) => { + setSelectedRole(e.target.value); + }; + + return ( + + + + + {/* Avatar */} + {/* */} + + {name} +
+ {email} +
+ + + + + + + Назначить роль + + +
+ + Выберите роль: + + + + + +
+
+ + + + +
+ +
+
+
+ + ); +} \ No newline at end of file diff --git a/src/components/users.js b/src/components/users.js new file mode 100644 index 0000000..207ead7 --- /dev/null +++ b/src/components/users.js @@ -0,0 +1,70 @@ +import React from "react"; +import { Container, Row, Col, Button, Navbar, Image,Dropdown,DropdownButton, CardBody, Card, CardTitle, CardHeader, FormGroup, FormLabel, FormControl, FormSelect } from 'react-bootstrap'; +import UserCard from '../components/userCard' +// import { BsPencilFill } from "react-icons/bs"; + +export default function UsersPage(){ + const data=[ + { + key: '1', + name: 'Иванов Иван', + role: 'Студент', + email: 'exemple@gmail.com' + + }, + { + key: '2', + + name: 'Чел Человский', + role: 'Преподаватель', + email: 'exemple@gmail.com' + }, + { + key: '3', + + name: 'Рандом юзер', + role: 'Пользователь', + email: 'exemple@gmail.com' + } + ] + return( + + + + Фильтры + + + + Поиск по имени пользователя + + + + Сортировка по роли + + + + + + + + + + + +
+ {data.map((user)=>( + + ))} +
+
+
+ + +
+ ); +} \ No newline at end of file diff --git a/src/requests/requestsMetods.js b/src/requests/requestsMetods.js new file mode 100644 index 0000000..c8511eb --- /dev/null +++ b/src/requests/requestsMetods.js @@ -0,0 +1,39 @@ +import React from "react"; + +export async function LoginFetch(data){ + // try { + const response = await fetch('http://89.111.174.112:8181/user/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }); + + return response.json(); + +} +export async function LogoutFetch(token){ + + const response = await fetch('http://89.111.174.112:8181/user/logout', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + + return response +} +export async function ProfileFetch(token){ + + const response = await fetch('http://89.111.174.112:8181/user/getProfile', { + method: 'GET', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + + return response.json() +} \ No newline at end of file diff --git a/src/router/router.js b/src/router/router.js index 481fe2d..73af4dc 100644 --- a/src/router/router.js +++ b/src/router/router.js @@ -2,6 +2,7 @@ import React from 'react'; import { Routes, Route} from 'react-router'; import Header from '../components/header'; import Login from '../components/login'; +import UsersPage from '../components/users'; export default function Router(){ return( @@ -13,6 +14,12 @@ export default function Router(){ }/> + +
+ + + }/> ); } \ No newline at end of file