diff --git a/.gitignore b/.gitignore index 287ce4cf..5969a41a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ .env.development.local .env.test.local .env.production.local +.env npm-debug.log* yarn-debug.log* diff --git a/package-lock.json b/package-lock.json index 07b6ceef..bb6e9960 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.14.19", + "@mui/lab": "^5.0.0-alpha.157", "@mui/material": "^5.14.19", "@reduxjs/toolkit": "^2.0.1", "@testing-library/jest-dom": "^5.17.0", @@ -20,11 +21,14 @@ "@types/node": "^16.18.66", "@types/react": "^18.2.39", "@types/react-dom": "^18.2.17", + "firebase": "^10.7.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^9.0.4", "react-router-dom": "^6.20.1", "react-scripts": "5.0.1", + "redux": "^5.0.0", + "redux-saga": "^1.2.3", "typescript": "^4.9.5", "web-vitals": "^2.1.4" } @@ -2547,6 +2551,504 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.0.tgz", + "integrity": "sha512-Locv8gAqx0e+GX/0SI3dzmBY5e9kjVDtD+3zCFLJ0tH2hJwuCAiL+5WkHuxKj92rqQj/rvkBUCfA1ewlX2hehg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.6.tgz", + "integrity": "sha512-4MqpVLFkGK7NJf/5wPEEP7ePBJatwYpyjgJ+wQHQGHfzaCDgntOnl9rL2vbVGGKCnRqWtZDIWhctB86UWXaX2Q==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-types": "0.8.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.0.tgz", + "integrity": "sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==" + }, + "node_modules/@firebase/app": { + "version": "0.9.25", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.25.tgz", + "integrity": "sha512-fX22gL5USXhOK21Hlh3oTeOzQZ6th6S2JrjXNEpBARmwzuUkqmVGVdsOCIFYIsLpK0dQE3o8xZnLrRg5wnzZ/g==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.1.tgz", + "integrity": "sha512-zi3vbM5tb/eGRWyiqf+1DXbxFu9Q07dnm46rweodgUpH9B8svxYkHfNwYWx7F5mjHU70SQDuaojH1We5ws9OKA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.8.tgz", + "integrity": "sha512-EaETtChR4UgMokJFw+r6jfcIyCTUZSe0a6ivF37D9MxlG9G3wzK1COyXgxoX96GzXmDPc2aubX4PxCrdVHhrnA==", + "dependencies": { + "@firebase/app-check": "0.8.1", + "@firebase/app-check-types": "0.5.0", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.0.tgz", + "integrity": "sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.25.tgz", + "integrity": "sha512-B/JtCp1FsTuzlh1tIGQpYM2AXps21/zlzpFsk5LRsROOTRhBcR2N45AyaONPFD06C0yS0Tw19foxADzHyOSC3A==", + "dependencies": { + "@firebase/app": "0.9.25", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" + }, + "node_modules/@firebase/auth": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.5.1.tgz", + "integrity": "sha512-sVi7rq2YneLGJFqHa5S6nDfCHix9yuVV3RLhj/pWPlB4a36ofXal4E6PJwpeMc8uLjWEr1aovYN1jkXWNB6Avw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.1.tgz", + "integrity": "sha512-rgDZnrDoekRvtzXVji8Z61wxxkof6pTkjYEkybILrjM8tGP9tx4xa9qGpF4ax3AzF+rKr7mIa9NnoXEK4UNqmQ==", + "dependencies": { + "@firebase/auth": "1.5.1", + "@firebase/auth-types": "0.12.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", + "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.0.tgz", + "integrity": "sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz", + "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==", + "dependencies": { + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.2.tgz", + "integrity": "sha512-8X6NBJgUQzDz0xQVaCISoOLINKat594N2eBbMR3Mu/MH/ei4WM+aAMlsNzngF22eljXu1SILP5G3evkyvsG3Ng==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.0", + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.2.tgz", + "integrity": "sha512-09ryJnXDvuycsxn8aXBzLhBTuCos3HEnCOBWY6hosxfYlNCGnLvG8YMlbSAt5eNhf7/00B095AEfDsdrrLjxqA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/database": "1.0.2", + "@firebase/database-types": "1.0.0", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.0.tgz", + "integrity": "sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==", + "dependencies": { + "@firebase/app-types": "0.9.0", + "@firebase/util": "1.9.3" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.4.0.tgz", + "integrity": "sha512-VeDXD9PUjvcWY1tInBOMTIu2pijR3YYy+QAe5cxCo1Q1vW+aA/mpQHhebPM1J6b4Zd1MuUh8xpBRvH9ujKR56A==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "@firebase/webchannel-wrapper": "0.10.5", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "engines": { + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.23.tgz", + "integrity": "sha512-uUTBiP0GLVBETaOCfB11d33OWB8x1r2G1Xrl0sRK3Va0N5LJ/GRvKVSGfM7VScj+ypeHe8RpdwKoCqLpN1e+uA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/firestore": "4.4.0", + "@firebase/firestore-types": "3.0.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.0.tgz", + "integrity": "sha512-Meg4cIezHo9zLamw0ymFYBD4SMjLb+ZXIbuN7T7ddXN6MGoICmOTq3/ltdCGoDCS2u+H1XJs2u/cYp75jsX9Qw==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.0.tgz", + "integrity": "sha512-n1PZxKnJ++k73Q8khTPwihlbeKo6emnGzE0hX6QVQJsMq82y/XKmNpw2t/q30VJgwaia3ZXU1fd1C5wHncL+Zg==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.0", + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.6.tgz", + "integrity": "sha512-RQpO3yuHtnkqLqExuAT2d0u3zh8SDbeBYK5EwSCBKI9mjrFeJRXBnd3pEG+x5SxGJLy56/5pQf73mwt0OuH5yg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/functions": "0.11.0", + "@firebase/functions-types": "0.6.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.0.tgz", + "integrity": "sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.4.tgz", + "integrity": "sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "idb": "7.0.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.4.tgz", + "integrity": "sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/installations-types": "0.5.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.0.tgz", + "integrity": "sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/installations/node_modules/idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" + }, + "node_modules/@firebase/logger": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", + "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.5.tgz", + "integrity": "sha512-i/rrEI2k9ueFhdIr8KQsptWGskrsnkC5TkohCTrJKz9P0C/PbNv14IAMkwhMJTqIur5VwuOnrUkc9Kdz7awekw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.5.tgz", + "integrity": "sha512-qHQZxm4hEG8/HFU/ls5/bU+rpnlPDoZoqi3ATMeb6s4hovYV9+PfV5I7ZrKV5eFFv47Hx1PWLe5uPnS4e7gMwQ==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/messaging": "0.12.5", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.0.tgz", + "integrity": "sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==" + }, + "node_modules/@firebase/performance": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.4.tgz", + "integrity": "sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.4.tgz", + "integrity": "sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/performance": "0.6.4", + "@firebase/performance-types": "0.2.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.0.tgz", + "integrity": "sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.4.tgz", + "integrity": "sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.4.tgz", + "integrity": "sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-types": "0.3.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.0.tgz", + "integrity": "sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==" + }, + "node_modules/@firebase/storage": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.12.0.tgz", + "integrity": "sha512-SGs02Y/mmWBRsqZiYLpv4Sf7uZYZzMWVNN+aKiDqPsFBCzD6hLvGkXz+u98KAl8FqcjgB8BtSu01wm4pm76KHA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0", + "undici": "5.26.5" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.3.tgz", + "integrity": "sha512-WNtjYPhpOA1nKcRu5lIodX0wZtP8pI0VxDJnk6lr+av7QZNS1s6zvr+ERDTve+Qu4Hq/ZnNaf3kBEQR2ccXn6A==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/storage": "0.12.0", + "@firebase/storage-types": "0.8.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.0.tgz", + "integrity": "sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", + "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.10.5.tgz", + "integrity": "sha512-eSkJsnhBWv5kCTSU1tSUVl9mpFu+5NXXunZc83le8GMjMlsWwQArSc7cJJ4yl+aDFY0NGLi0AjZWMn1axOrkRg==" + }, "node_modules/@floating-ui/core": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", @@ -2581,6 +3083,73 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.12.tgz", + "integrity": "sha512-Um5MBuge32TS3lAKX02PGCnFM4xPT996yLgZNb5H03pn6NyJ4Iwn5YcPq6Jj9yxGRk7WOgaZFtVRH5iTdYBeUg==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -3401,6 +3970,77 @@ } } }, + "node_modules/@mui/lab": { + "version": "5.0.0-alpha.157", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.157.tgz", + "integrity": "sha512-gY7UM2kNSxiVLfsm0o6HG2G5rM2Vr47prJhDCazY+VG/NOSRc8CG7la6dpL9WDTJhotEZdWwfj1FOUxTonmuQA==", + "dependencies": { + "@babel/runtime": "^7.23.5", + "@mui/base": "5.0.0-beta.28", + "@mui/system": "^5.15.1", + "@mui/types": "^7.2.11", + "@mui/utils": "^5.15.1", + "clsx": "^2.0.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material": ">=5.10.11", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/lab/node_modules/@mui/base": { + "version": "5.0.0-beta.28", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.28.tgz", + "integrity": "sha512-KIoSc5sUFceeCaZTq5MQBapFzhHqMo4kj+4azWaCAjorduhcRQtN+BCgVHmo+gvEjix74bUfxwTqGifnu2fNTg==", + "dependencies": { + "@babel/runtime": "^7.23.5", + "@floating-ui/react-dom": "^2.0.4", + "@mui/types": "^7.2.11", + "@mui/utils": "^5.15.1", + "@popperjs/core": "^2.11.8", + "clsx": "^2.0.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/material": { "version": "5.14.19", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.19.tgz", @@ -3451,12 +4091,12 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@mui/private-theming": { - "version": "5.14.19", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.19.tgz", - "integrity": "sha512-U9w39VpXLGVM8wZlUU/47YGTsBSk60ZQRRxQZtdqPfN1N7OVllQeN4cEKZKR8PjqqR3aYRcSciQ4dc6CttRoXQ==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.1.tgz", + "integrity": "sha512-wTbzuy5KjSvCPE9UVJktWHJ0b/tD5biavY9wvF+OpYDLPpdXK52vc1hTDxSbdkHIFMkJExzrwO9GvpVAHZBnFQ==", "dependencies": { - "@babel/runtime": "^7.23.4", - "@mui/utils": "^5.14.19", + "@babel/runtime": "^7.23.5", + "@mui/utils": "^5.15.1", "prop-types": "^15.8.1" }, "engines": { @@ -3477,11 +4117,11 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.14.19", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.19.tgz", - "integrity": "sha512-jtj/Pyn/bS8PM7NXdFNTHWZfE3p+vItO4/HoQbUeAv3u+cnWXcTBGHHY/xdIn446lYGFDczTh1YyX8G4Ts0Rtg==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.1.tgz", + "integrity": "sha512-7WDZTJLqGexWDjqE9oAgjU8ak6hEtUw2yQU7SIYID5kLVO2Nj/Wi/KicbLsXnTsJNvSqePIlUIWTBSXwWJCPZw==", "dependencies": { - "@babel/runtime": "^7.23.4", + "@babel/runtime": "^7.23.5", "@emotion/cache": "^11.11.0", "csstype": "^3.1.2", "prop-types": "^15.8.1" @@ -3508,15 +4148,15 @@ } }, "node_modules/@mui/system": { - "version": "5.14.19", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.19.tgz", - "integrity": "sha512-4e3Q+2nx+vgEsd0h5ftxlZGB7XtkkPos/zWqCqnxUs1l/T70s0lF2YNrWHHdSQ7LgtBu0eQ0qweZG2pR7KwkAw==", - "dependencies": { - "@babel/runtime": "^7.23.4", - "@mui/private-theming": "^5.14.19", - "@mui/styled-engine": "^5.14.19", - "@mui/types": "^7.2.10", - "@mui/utils": "^5.14.19", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.1.tgz", + "integrity": "sha512-LAnP0ls69rqW9eBgI29phIx/lppv+WDGI7b3EJN7VZIqw0RezA0GD7NRpV12BgEYJABEii6z5Q9B5tg7dsX0Iw==", + "dependencies": { + "@babel/runtime": "^7.23.5", + "@mui/private-theming": "^5.15.1", + "@mui/styled-engine": "^5.15.1", + "@mui/types": "^7.2.11", + "@mui/utils": "^5.15.1", "clsx": "^2.0.0", "csstype": "^3.1.2", "prop-types": "^15.8.1" @@ -3547,9 +4187,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.10", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.10.tgz", - "integrity": "sha512-wX1vbDC+lzF7FlhT6A3ffRZgEoKWPF8VqRoTu4lZwouFX2t90KyCMsgepMw5DxLak1BSp/KP86CmtZttikb/gQ==", + "version": "7.2.11", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.11.tgz", + "integrity": "sha512-KWe/QTEsFFlFSH+qRYf3zoFEj3z67s+qAuSnMMg+gFwbxG7P96Hm6g300inQL1Wy///gSRb8juX7Wafvp93m3w==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" }, @@ -3560,11 +4200,11 @@ } }, "node_modules/@mui/utils": { - "version": "5.14.19", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.19.tgz", - "integrity": "sha512-qAHvTXzk7basbyqPvhgWqN6JbmI2wLB/mf97GkSlz5c76MiKYV6Ffjvw9BjKZQ1YRb8rDX9kgdjRezOcoB91oQ==", + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.1.tgz", + "integrity": "sha512-V1/d0E3Bju5YdB59HJf2G0tnHrFEvWLN+f8hAXp9+JSNy/LC2zKyqUfPPahflR6qsI681P8G9r4mEZte/SrrYA==", "dependencies": { - "@babel/runtime": "^7.23.4", + "@babel/runtime": "^7.23.5", "@types/prop-types": "^15.7.11", "prop-types": "^15.8.1", "react-is": "^18.2.0" @@ -3709,6 +4349,119 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@redux-saga/core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz", + "integrity": "sha512-U1JO6ncFBAklFTwoQ3mjAeQZ6QGutsJzwNBjgVLSWDpZTRhobUzuVDS1qH3SKGJD8fvqoaYOjp6XJ3gCmeZWgA==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@redux-saga/deferred": "^1.2.1", + "@redux-saga/delay-p": "^1.2.1", + "@redux-saga/is": "^1.1.3", + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1", + "redux": "^4.0.4", + "typescript-tuple": "^2.2.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/redux-saga" + } + }, + "node_modules/@redux-saga/core/node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/@redux-saga/deferred": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz", + "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==" + }, + "node_modules/@redux-saga/delay-p": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz", + "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==", + "dependencies": { + "@redux-saga/symbols": "^1.1.3" + } + }, + "node_modules/@redux-saga/is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz", + "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==", + "dependencies": { + "@redux-saga/symbols": "^1.1.3", + "@redux-saga/types": "^1.2.1" + } + }, + "node_modules/@redux-saga/symbols": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz", + "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==" + }, + "node_modules/@redux-saga/types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz", + "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==" + }, "node_modules/@reduxjs/toolkit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.0.1.tgz", @@ -8617,6 +9370,39 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase": { + "version": "10.7.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.7.1.tgz", + "integrity": "sha512-Mlt7y7zQ43FtKp4SCyYie3tnrOL3UMF2XXiV4ZXMrC0d0wtcOYmABuybhkJpJCKILpdekxr39wjnaai0DZlWFg==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-compat": "0.2.6", + "@firebase/app": "0.9.25", + "@firebase/app-check": "0.8.1", + "@firebase/app-check-compat": "0.3.8", + "@firebase/app-compat": "0.2.25", + "@firebase/app-types": "0.9.0", + "@firebase/auth": "1.5.1", + "@firebase/auth-compat": "0.5.1", + "@firebase/database": "1.0.2", + "@firebase/database-compat": "1.0.2", + "@firebase/firestore": "4.4.0", + "@firebase/firestore-compat": "0.3.23", + "@firebase/functions": "0.11.0", + "@firebase/functions-compat": "0.3.6", + "@firebase/installations": "0.6.4", + "@firebase/installations-compat": "0.2.4", + "@firebase/messaging": "0.12.5", + "@firebase/messaging-compat": "0.2.5", + "@firebase/performance": "0.6.4", + "@firebase/performance-compat": "0.2.4", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-compat": "0.2.4", + "@firebase/storage": "0.12.0", + "@firebase/storage-compat": "0.3.3", + "@firebase/util": "1.9.3" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -12450,6 +13236,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -12475,6 +13266,11 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -14731,6 +15527,29 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -15250,6 +16069,14 @@ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.0.tgz", "integrity": "sha512-blLIYmYetpZMET6Q6uCY7Jtl/Im5OBldy+vNPauA8vvsdqyt66oep4EUpAMWNHauTC6xa9JuRPhRB72rY82QGA==" }, + "node_modules/redux-saga": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz", + "integrity": "sha512-HDe0wTR5nhd8Xr5xjGzoyTbdAw6rjy1GDplFt3JKtKN8/MnkQSRqK/n6aQQhpw5NI4ekDVOaW+w4sdxPBaCoTQ==", + "dependencies": { + "@redux-saga/core": "^1.2.3" + } + }, "node_modules/redux-thunk": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", @@ -17139,6 +17966,27 @@ "node": ">=4.2.0" } }, + "node_modules/typescript-compare": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz", + "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==", + "dependencies": { + "typescript-logic": "^0.0.0" + } + }, + "node_modules/typescript-logic": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", + "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==" + }, + "node_modules/typescript-tuple": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz", + "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==", + "dependencies": { + "typescript-compare": "^0.0.2" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -17158,6 +18006,17 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" }, + "node_modules/undici": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.26.5.tgz", + "integrity": "sha512-cSb4bPFd5qgR7qr2jYAi0hlX9n5YKK2ONKkLFkxl+v/9BvC0sOpZjBHDBSXc5lWAf5ty9oZdRXytBIHzgUcerw==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index a821901d..12343f88 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,14 @@ "@types/node": "^16.18.66", "@types/react": "^18.2.39", "@types/react-dom": "^18.2.17", + "firebase": "^10.7.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^9.0.4", "react-router-dom": "^6.20.1", "react-scripts": "5.0.1", + "redux": "^5.0.0", + "redux-saga": "^1.2.3", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 00000000..a3300d11 Binary files /dev/null and b/public/favicon.png differ diff --git a/public/index.html b/public/index.html index 6252f08e..1477a186 100644 --- a/public/index.html +++ b/public/index.html @@ -3,7 +3,7 @@ - React App + Dear Diary diff --git a/src/components/DiaryCard/DiaryCard.tsx b/src/components/DiaryCard/DiaryCard.tsx index 2837bc71..c5d846eb 100644 --- a/src/components/DiaryCard/DiaryCard.tsx +++ b/src/components/DiaryCard/DiaryCard.tsx @@ -7,16 +7,15 @@ import { useState } from 'react'; const styles = { card: { - width:{ xs: '30%', sm: '271px'}, - padding: '20px', borderRadius: '6px', - mb: '35px', + padding: '10px', }, title: { fontFamily: 'Public Sans', fontSize: '18px', fontWeight: '500', mb: '10px', + wordWrap: 'break-word', }, description: { fontFamily: 'Public Sans', @@ -52,7 +51,7 @@ function DiaryCard(DiaryCardProps: DiaryCardProps) { setIsExpanded(!isExpanded); }; - const displayDescription = isExpanded ? DiaryCardProps.description : `${DiaryCardProps.description.slice(0, 100)}...`; + const displayDescription = isExpanded ? DiaryCardProps.description : `${DiaryCardProps.description.slice(0, 100)}`; return ( @@ -67,9 +66,11 @@ function DiaryCard(DiaryCardProps: DiaryCardProps) { - + {DiaryCardProps.description.length > 100 && ( + + )} ) diff --git a/src/index.tsx b/src/index.tsx index 88b4f642..c5fa117d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,13 +3,17 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; +import store from './redux/store'; +import { Provider } from 'react-redux'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( - + + + ); diff --git a/src/pages/DiaryHome/DiaryForm/DiaryForm.tsx b/src/pages/DiaryHome/DiaryForm/DiaryForm.tsx index 244c8d6b..e675b9f9 100644 --- a/src/pages/DiaryHome/DiaryForm/DiaryForm.tsx +++ b/src/pages/DiaryHome/DiaryForm/DiaryForm.tsx @@ -1,6 +1,8 @@ import { Box, TextField, Button, DialogTitle, DialogContent, DialogActions, Typography } from '@mui/material'; import DisabledByDefaultRoundedIcon from '@mui/icons-material/DisabledByDefaultRounded'; import { useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { addDiary } from '../../../redux/diarySlice'; const styles = { submitNew: { @@ -53,37 +55,47 @@ close:{ } }; -interface Diary { - title: string; - description: string; -} + + interface DiaryFormProps { onClose: () => void; - setDiaries: React.Dispatch>; - diaries: Diary[]; + onDiarySubmit: () => void; } -const DiaryForm: React.FC = ({onClose, setDiaries, diaries}) => { +const DiaryForm: React.FC = ({onClose, onDiarySubmit}) => { + const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); + const nickname = localStorage.getItem('nickname'); + + const dispatch = useDispatch(); const onsubmit = () => { if (title === "" || description === "") { alert("Please fill out all fields"); return; }else{ + console.log("Title: ", title); console.log("Description: ", description); - const existingEntries = JSON.parse(localStorage.getItem("diaries") || "[]"); // Get the existing entries - existingEntries.push({ title, description }); // Add the new entry to the existing entries - localStorage.setItem("diaries", JSON.stringify(existingEntries)); // Save the updated entries to local storage - setDiaries([...diaries, { title, description }]) + + if (nickname) { + interface Diary { + title: string; + description: string; + nickname: string; + } + + dispatch(addDiary({ title, description, nickname } as Diary)); + } + setTitle(""); setDescription(""); onClose(); + onDiarySubmit(); } }; diff --git a/src/pages/DiaryHome/DiaryHome.tsx b/src/pages/DiaryHome/DiaryHome.tsx index fd9ee9a1..e65b305b 100644 --- a/src/pages/DiaryHome/DiaryHome.tsx +++ b/src/pages/DiaryHome/DiaryHome.tsx @@ -1,11 +1,15 @@ -import React from 'react' +import React from 'react'; +import {useEffect} from 'react' import Background from '../../components/Background/Background'; -import { Box, Button, TextField, Typography, Dialog} from "@mui/material"; +import { Box, Button, TextField, Typography, Dialog} from "@mui/material"; import SearchIcon from '@mui/icons-material/Search'; import InputAdornment from '@mui/material/InputAdornment'; import Header from '../../components/Header/Header'; import DiaryForm from './DiaryForm/DiaryForm'; import DiaryCard from '../../components/DiaryCard/DiaryCard'; +import SnackBar from './SnackBar/SnackBar'; +import { useDispatch, useSelector } from 'react-redux'; +import { fetchDiaries } from '../../redux/diarySlice'; const styles = { home:{ fontFamily:"public sans", @@ -20,30 +24,31 @@ const styles = { box:{ display:"flex", - justifyContent:"spaceBetween", - height: '48px', - padding:'0' + flexDirection:{xs:"column", sm:"row"}, + justifyContent:"space-between", + width: 'calc(100% - 100px)', + gap: '10px', + padding:'0', + position: 'absolute', + left: '50px', + top:'200px', }, + textfield:{ - width: { xs: '40%', sm: '50%' }, - maxWidth: '50%', + width: { xs: '100%', sm: '50%' }, '& .MuiInputBase-root': { height: '48px', }, - position: 'absolute', - left: '50px', - top:'200px', + + backgroundColor: 'white', }, submibNew:{ backgroundColor: '#0092DD', - width: { xs: '30%', sm: '154px' }, + width: { xs: '100%', sm: '154px' }, height: '48px', - position: 'absolute', - right: '50px', - top:'200px', - + textTransform: 'none', }, dialog: { @@ -60,21 +65,28 @@ const styles = { } }, - diaryEntries: { + diaryEntries:{ + width: 'calc(100% - 100px)', + margin: '100px 10px 10px 50px', + padding: '0px 0px 50px 0px', position: 'absolute', - top: '300px', - left: '50px', - display: 'flex', - flexDirection: { xs: 'column', sm: 'row' }, - flexWrap: 'wrap', - justifyContent: 'space-between', - alignItems: 'flex-start', - gap: { xs: '0', sm: '35px'}, - marginRight: '50px', - } + top: { xs: '250px', sm: '200px' }, + columnCount: { xs: 1, sm: 2, md: 3, lg: 4, xl: 6}, + columnGap: '35px', + + }, + +} +interface Diary { + title: string; + description: string; + nickname: string; } +interface RootState { + diaries: Diary[]; +} const DiaryHome = () => { @@ -87,11 +99,34 @@ const DiaryHome = () => { setOpen(true); } - const handleColse = () => { + const handleFormColse = () => { setOpen(false); } - - const [diaries, setDiaries] = React.useState(JSON.parse(localStorage.getItem("diaries") || "[]")); // Get the existing entries + const [openSnackbar, setOpenSnackbar] = React.useState(false); + const [submissionTime, setSubmissionTime] = React.useState(null); + const handleCloseSnackbar = () => { + setOpenSnackbar(false); + }; + + const dispatch = useDispatch(); + useEffect(() => { + const nickname = localStorage.getItem('nickname'); + dispatch(fetchDiaries({ nickname })); + }, []); // Empty dependency array ensures the effect runs only on mount + + + const [searchText, setSearchText] = React.useState(''); + const diaries = useSelector((state: RootState) => state.diaries); + const [filteredDiaries, setFilteredDiaries] = React.useState([]); + + useEffect(() => { + setFilteredDiaries(diaries.filter((diary) => { + return ( + diary.title.toLowerCase().includes(searchText.toLowerCase()) || + diary.description.toLowerCase().includes(searchText.toLowerCase()) + ); + })); + }, [diaries, searchText]); return ( @@ -108,7 +143,7 @@ const DiaryHome = () => { - @@ -117,6 +152,8 @@ const DiaryHome = () => { ), }} sx={styles.textfield} + value={searchText} + onChange={(e) => setSearchText(e.target.value)} /> - - + + { + setOpenSnackbar(true); + setSubmissionTime(new Date()); + }}/> - {diaries.map((diary: { title: string; description: string }) => ( - - ))} + {filteredDiaries.map((diaryEntry, index) => ( + + + + ))} + + ) diff --git a/src/pages/DiaryHome/SnackBar/SnackBar.tsx b/src/pages/DiaryHome/SnackBar/SnackBar.tsx new file mode 100644 index 00000000..4998491e --- /dev/null +++ b/src/pages/DiaryHome/SnackBar/SnackBar.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import Snackbar from '@mui/material/Snackbar'; +import IconButton from '@mui/material/IconButton'; +import CloseIcon from '@mui/icons-material/Close'; +import CheckOutlinedIcon from '@mui/icons-material/CheckOutlined'; + +interface snackBarProps { + openSnackbar: boolean; + handleCloseSnackbar: () => void; + submissionTime: Date | null; +} + +const SnackBar: React.FC = ({openSnackbar, handleCloseSnackbar, submissionTime})=> { + return ( +
+ + + {` Record saved successfully ${submissionTime ? `${Math.floor((new Date().getTime() - submissionTime.getTime()) / 60000)} mins ago` : ''}`} + + } + action={ + + + + + + } + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + sx={ + { + '& .MuiSnackbarContent-root': { + backgroundColor: '#FFFFFF', + color: '#000', + borderRadius: '6px', + }, + } + + } + /> +
+ ) +} + +export default SnackBar \ No newline at end of file diff --git a/src/pages/SignIn/SignIn.tsx b/src/pages/SignIn/SignIn.tsx index d58573a5..9bad7fe7 100644 --- a/src/pages/SignIn/SignIn.tsx +++ b/src/pages/SignIn/SignIn.tsx @@ -5,6 +5,8 @@ import Background from "../../components/Background/Background"; import { Box, Button, TextField, Typography, Card, CardContent} from "@mui/material"; import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; import {generateRandomName} from '../../utility/generateRandomName'; +import { useDispatch } from "react-redux"; +import { setDiaries } from "../../redux/diarySlice"; const styles = { card: { @@ -76,10 +78,12 @@ const styles = { backgroundColor: '#0092DD', opacity: '65%', }, + textTransform: 'none', }, continueButton: { backgroundColor: '#0092DD', opacity: '65%', + textTransform: 'none', }, } @@ -98,12 +102,13 @@ const SignIn: React.FC = () => { }; const navigate = useNavigate(); - + const dispatch = useDispatch(); const handleContinueClick = () => { if (text.trim() === '') { alert('Please enter a nickname'); } else { localStorage.setItem('nickname', text); + dispatch(setDiaries([])); navigate('/home'); } } diff --git a/src/redux/Sagas/diarySagas.tsx b/src/redux/Sagas/diarySagas.tsx new file mode 100644 index 00000000..b893f936 --- /dev/null +++ b/src/redux/Sagas/diarySagas.tsx @@ -0,0 +1,40 @@ +import { eventChannel } from 'redux-saga'; +import { call, put, take, takeEvery } from 'redux-saga/effects'; +import { PayloadAction } from '@reduxjs/toolkit'; +import { collection, addDoc, onSnapshot, query, where } from 'firebase/firestore'; +import db from '../../utility/firebase'; +import { addDiary, setDiaries, fetchDiaries } from '../diarySlice'; + +interface Diary { + title: string; + description: string; + nickname: string; + } + +function* addDiarySaga(action: PayloadAction) { + const diariesCollection = collection(db, 'diaries'); + yield call(addDoc, diariesCollection, action.payload); +} +function* fetchDiariesSaga(action: PayloadAction<{ nickname: string }>): Generator { + const { nickname } = action.payload; + const diariesCollection = collection(db, 'diaries'); + const q = query(diariesCollection, where("nickname", "==", nickname)); + + const channel = eventChannel(emitter => { + const unsubscribe = onSnapshot(q, snapshot => { + emitter(snapshot.docs.map(doc => doc.data())); + }); + + return () => unsubscribe(); + }); + + while (true) { + const diariesArray = yield take(channel); + yield put(setDiaries(diariesArray)); + } +} + +export function* watchDiarySagas() { + yield takeEvery(addDiary.type, addDiarySaga); + yield takeEvery(fetchDiaries.type, fetchDiariesSaga); +} diff --git a/src/redux/diarySlice.tsx b/src/redux/diarySlice.tsx new file mode 100644 index 00000000..02ee8dae --- /dev/null +++ b/src/redux/diarySlice.tsx @@ -0,0 +1,26 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; + +interface Diary { + title: string; + description: string; + nickname: string; +} + +const diarySlice = createSlice({ + name: 'diaries', + initialState: [] as Diary[], + + reducers: { + addDiary: (state, action: PayloadAction) => { + state.push(action.payload); + }, + setDiaries: (state, action: PayloadAction) => { + return action.payload; + }, + fetchDiaries: (state, action: PayloadAction<{ nickname: string | null }>) => {}, + }, +}); + +export const { addDiary, setDiaries, fetchDiaries } = diarySlice.actions; + +export default diarySlice.reducer; diff --git a/src/redux/store.tsx b/src/redux/store.tsx new file mode 100644 index 00000000..8daee563 --- /dev/null +++ b/src/redux/store.tsx @@ -0,0 +1,19 @@ +import { configureStore } from '@reduxjs/toolkit'; +import diaryReducer from './diarySlice'; +import createSagaMiddleware from 'redux-saga'; +import { watchDiarySagas } from './Sagas/diarySagas'; + +const sagaMiddleware = createSagaMiddleware(); +const middleware: any[] = [sagaMiddleware]; + +const store = configureStore({ + reducer: { + diaries: diaryReducer, + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().concat(middleware), +}); + +sagaMiddleware.run(watchDiarySagas); + +export default store; diff --git a/src/utility/firebase.tsx b/src/utility/firebase.tsx new file mode 100644 index 00000000..3e06f11a --- /dev/null +++ b/src/utility/firebase.tsx @@ -0,0 +1,20 @@ +import { initializeApp } from "firebase/app"; +import { getFirestore } from "firebase/firestore"; + +const firebaseConfig = { + apiKey: process.env.REACT_APP_FIREBASE_API_KEY, + authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN, + projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID, + storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.REACT_APP_FIREBASE_APP_ID, + }; + + +// Initialize Firebase +const app = initializeApp(firebaseConfig); + +// Get a reference to the database service +const db = getFirestore(app); + +export default db; \ No newline at end of file