feat: add redux store

This commit is contained in:
Aidar Shaikhutdin @makeweb.space 2023-05-05 20:13:27 +06:00
parent a43bbe719a
commit 13a378b45c
19 changed files with 440 additions and 65 deletions

245
package-lock.json generated
View File

@ -8,11 +8,13 @@
"name": "aurawebapp",
"version": "0.0.0",
"dependencies": {
"@reduxjs/toolkit": "^1.9.5",
"i18next": "^22.4.15",
"react": "^18.2.0",
"react-circular-progressbar": "^2.1.0",
"react-dom": "^18.2.0",
"react-i18next": "^12.2.2",
"react-redux": "^8.0.5",
"react-router-dom": "^6.11.0"
},
"devDependencies": {
@ -946,6 +948,29 @@
"node": ">= 8"
}
},
"node_modules/@reduxjs/toolkit": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz",
"integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==",
"dependencies": {
"immer": "^9.0.21",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"reselect": "^4.1.8"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18",
"react-redux": "^7.2.1 || ^8.0.2"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@remix-run/router": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.0.tgz",
@ -960,6 +985,15 @@
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
"dev": true
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@ -969,14 +1003,12 @@
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
},
"node_modules/@types/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
"integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@ -987,7 +1019,7 @@
"version": "18.2.1",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.1.tgz",
"integrity": "sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==",
"dev": true,
"devOptional": true,
"dependencies": {
"@types/react": "*"
}
@ -1016,8 +1048,7 @@
"node_modules/@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
"dev": true
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
},
"node_modules/@types/semver": {
"version": "7.3.13",
@ -1025,6 +1056,11 @@
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
"dev": true
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz",
@ -1447,8 +1483,7 @@
"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==",
"dev": true
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
"node_modules/debug": {
"version": "4.3.4",
@ -2079,6 +2114,19 @@
"node": ">=4"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/hoist-non-react-statics/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/html-parse-stringify": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
@ -2118,6 +2166,15 @@
"node": ">= 4"
}
},
"node_modules/immer": {
"version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -2637,6 +2694,49 @@
}
}
},
"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-redux": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
"integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
"dependencies": {
"@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/use-sync-external-store": "^0.0.3",
"hoist-non-react-statics": "^3.3.2",
"react-is": "^18.0.0",
"use-sync-external-store": "^1.0.0"
},
"peerDependencies": {
"@types/react": "^16.8 || ^17.0 || ^18.0",
"@types/react-dom": "^16.8 || ^17.0 || ^18.0",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0",
"react-native": ">=0.59",
"redux": "^4"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@ -2676,11 +2776,32 @@
"react-dom": ">=16.8"
}
},
"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-thunk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
"integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
"peerDependencies": {
"redux": "^4"
}
},
"node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"node_modules/reselect": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
"integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
},
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@ -2994,6 +3115,14 @@
"punycode": "^2.1.0"
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/vite": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz",
@ -3686,6 +3815,17 @@
"fastq": "^1.6.0"
}
},
"@reduxjs/toolkit": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz",
"integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==",
"requires": {
"immer": "^9.0.21",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"reselect": "^4.1.8"
}
},
"@remix-run/router": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.0.tgz",
@ -3697,6 +3837,15 @@
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
"dev": true
},
"@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"requires": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@ -3706,14 +3855,12 @@
"@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
},
"@types/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
"integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
"dev": true,
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@ -3724,7 +3871,7 @@
"version": "18.2.1",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.1.tgz",
"integrity": "sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==",
"dev": true,
"devOptional": true,
"requires": {
"@types/react": "*"
}
@ -3753,8 +3900,7 @@
"@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
"dev": true
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
},
"@types/semver": {
"version": "7.3.13",
@ -3762,6 +3908,11 @@
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
"dev": true
},
"@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"@typescript-eslint/eslint-plugin": {
"version": "5.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz",
@ -4026,8 +4177,7 @@
"csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
"dev": true
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
"debug": {
"version": "4.3.4",
@ -4496,6 +4646,21 @@
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
},
"dependencies": {
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}
}
},
"html-parse-stringify": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
@ -4518,6 +4683,11 @@
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
"dev": true
},
"immer": {
"version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA=="
},
"import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -4871,6 +5041,24 @@
"html-parse-stringify": "^3.0.1"
}
},
"react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"react-redux": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
"integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
"requires": {
"@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/use-sync-external-store": "^0.0.3",
"hoist-non-react-statics": "^3.3.2",
"react-is": "^18.0.0",
"use-sync-external-store": "^1.0.0"
}
},
"react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
@ -4894,11 +5082,30 @@
"react-router": "6.11.0"
}
},
"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==",
"requires": {
"@babel/runtime": "^7.9.2"
}
},
"redux-thunk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
"integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
"requires": {}
},
"regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"reselect": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
"integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
},
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@ -5099,6 +5306,12 @@
"punycode": "^2.1.0"
}
},
"use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"requires": {}
},
"vite": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz",

View File

@ -10,11 +10,13 @@
"preview": "vite preview"
},
"dependencies": {
"@reduxjs/toolkit": "^1.9.5",
"i18next": "^22.4.15",
"react": "^18.2.0",
"react-circular-progressbar": "^2.1.0",
"react-dom": "^18.2.0",
"react-i18next": "^12.2.2",
"react-redux": "^8.0.5",
"react-router-dom": "^6.11.0"
},
"devDependencies": {

3
src/auth/AuthContext.ts Normal file
View File

@ -0,0 +1,3 @@
import { createContext } from 'react'
export const AuthContext = createContext<unknown>({})

10
src/auth/AuthProvider.tsx Normal file
View File

@ -0,0 +1,10 @@
import { AuthContext } from './AuthContext'
export function AuthProvider({ children }: React.PropsWithChildren<unknown>): JSX.Element {
const auth = {}
return (
<AuthContext.Provider value={auth}>
{children}
</AuthContext.Provider>
)
}

3
src/auth/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from './useAuth'
export * from './AuthContext'
export * from './AuthProvider'

4
src/auth/useAuth.ts Normal file
View File

@ -0,0 +1,4 @@
import { useContext } from 'react'
import { AuthContext } from './AuthContext'
export const useAuth = () => useContext(AuthContext)

View File

@ -1,4 +1,4 @@
import { Routes, Route, Navigate } from 'react-router-dom';
import { Routes, Route, Navigate } from 'react-router-dom'
import BirthdayPage from '../BirthdayPage'
import BirthtimePage from '../BirthtimePage'
import CreateProfilePage from '../CreateProfilePage'
@ -9,7 +9,7 @@ import Header from '../Header'
import routes from '../../routes'
import './styles.css'
function App() {
function App(): JSX.Element {
return (
<div className='container'>
<Header />

View File

@ -1,6 +1,8 @@
import { useEffect, useState } from 'react'
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { actions, RootState } from '../../store'
import routes from '../../routes'
import Policy from '../Policy'
import Purposes from '../Purposes'
@ -11,8 +13,9 @@ import './styles.css'
function BirthdayPage(): JSX.Element {
const { t } = useTranslation()
const dispatch = useDispatch()
const navigate = useNavigate()
const [birthdate, setBirthdate] = useState('')
const birthdate = useSelector((state: RootState) => state.birthdate)
const [isDisabled, setIsDisabled] = useState(true)
const links = [
{ text: 'EULA', href: 'https://aura.wit.life/terms' },
@ -21,14 +24,10 @@ function BirthdayPage(): JSX.Element {
]
const handleNext = () => navigate(routes.client.birthtime())
const handleValid = (birthdate: string) => {
setBirthdate(birthdate)
dispatch(actions.birthdate.update(birthdate))
setIsDisabled(false)
}
useEffect(() => {
console.log('birthdate', birthdate)
}, [birthdate])
return (
<section className='page'>
<Title variant='h3' className='mt-24'>{t('letsStart')}</Title>

View File

@ -1,6 +1,7 @@
import { useState, useEffect } from "react"
import { useNavigate } from "react-router-dom"
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { actions, RootState } from '../../store'
import Title from "../Title"
import MainButton from "../MainButton"
import { TimePicker } from "../DateTimePicker"
@ -9,26 +10,17 @@ import './styles.css'
function BirthtimePage(): JSX.Element {
const { t } = useTranslation()
const dispatch = useDispatch()
const navigate = useNavigate();
const [birthtime, setBirhtime] = useState('')
const [isDisabled, setIsDisabled] = useState(true)
const birthtime = useSelector((state: RootState) => state.birthtime)
const handleNext = () => navigate(routes.client.createProfile())
useEffect(() => {
if (!birthtime) {
setIsDisabled(true)
return
}
setIsDisabled(false)
console.log('birthtime', birthtime)
}, [birthtime])
const handleChange = (value: string) => dispatch(actions.birthtime.update(value))
return (
<section className='page'>
<Title variant="h2" className="mt-24">{t('bornTimeQuestion')}</Title>
<p className="description">{t('nasaDataUsing')}</p>
<TimePicker onChange={(value: string) => setBirhtime(value)}/>
<MainButton label={t('next')} onClick={handleNext} disabled={isDisabled}/>
<TimePicker value={birthtime} onChange={handleChange}/>
<MainButton label={t('next')} onClick={handleNext} disabled={!birthtime}/>
</section>
)
}

View File

@ -1,19 +1,19 @@
import { useEffect, useState } from "react"
import { normalize } from "./utils"
import { normalize, parse, format } from "./utils"
type TimePickerProps = {
value: string
onChange: (value: string) => void
}
export function TimePicker({ onChange }: TimePickerProps): JSX.Element {
const [hour, setHour] = useState('1')
const [minute, setMinute] = useState('0')
const [period, setPeriod] = useState('AM')
export function TimePicker({ value, onChange }: TimePickerProps): JSX.Element {
const parsedValue = parse(value)
const [hour, setHour] = useState(parsedValue.hour)
const [minute, setMinute] = useState(parsedValue.minute)
const [period, setPeriod] = useState(parsedValue.period)
useEffect(() => {
const formattedHour = period === 'AM' ? normalize(hour, 2) : String(Number(hour) + 12)
const formattedMinute = normalize(minute, 2)
onChange(`${formattedHour}:${formattedMinute}`)
onChange(format(hour, minute, period))
}, [hour, minute, period, onChange])
return (
@ -22,6 +22,7 @@ export function TimePicker({ onChange }: TimePickerProps): JSX.Element {
<div className="date-picker__field">
<select
className="date-picker__field-select"
value={hour}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setHour(e.target.value)}>
{Array.from(Array(12).keys()).map((hour) => (
<option key={hour} value={hour + 1}>{hour + 1}</option>
@ -31,10 +32,11 @@ export function TimePicker({ onChange }: TimePickerProps): JSX.Element {
<div className="date-picker__field">
<select
className="date-picker__field-select"
value={minute}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setMinute(e.target.value)}>
{Array.from(Array(60).keys()).map((minute) => {
return (
<option key={minute} value={minute}>{normalize(minute, 2)}</option>
<option key={minute} value={normalize(minute, 2)}>{normalize(minute, 2)}</option>
);
})}
</select>
@ -42,6 +44,7 @@ export function TimePicker({ onChange }: TimePickerProps): JSX.Element {
<div className="date-picker__field">
<select
className="date-picker__field-select"
value={period}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setPeriod(e.target.value)}>
<option value="AM">AM</option>
<option value="PM">PM</option>

View File

@ -21,3 +21,21 @@ export const isNotTheDate = (date: Date) => {
export const getDaysInMonth = (year: number, month: number): number => {
return new Date(year, month, 0).getDate();
}
export const format = (hour: string, minute: string, period: string) => {
const formattedHour = period === 'AM' ? normalize(hour, 2) : String(Number(hour) + 12)
const formattedMinute = normalize(minute, 2)
return `${formattedHour}:${formattedMinute}`
}
export const parse = (value: string) => {
const [hour, minute] = value.split(':')
const numHour = Number(hour)
const period = numHour < 12 ? 'AM' : 'PM'
const formattedHour = numHour > 12 ? String(numHour - 12) : String(numHour)
return {
hour: formattedHour,
minute,
period
}
}

View File

@ -1,6 +1,8 @@
import { useEffect, useState } from 'react'
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { actions, RootState } from '../../store'
import Title from '../Title'
import Policy from '../Policy'
import EmailInput from '../EmailInput'
@ -9,23 +11,20 @@ import routes from '../../routes'
function EmailEnterPage(): JSX.Element {
const { t } = useTranslation()
const dispatch = useDispatch()
const navigate = useNavigate()
const [email, setEmail] = useState('')
const email = useSelector((state: RootState) => state.email)
const [isDisabled, setIsDisabled] = useState(true)
const links = [
{ text: 'EULA', href: 'https://aura.wit.life/terms' },
{ text: 'Privacy Policy', href: 'https://aura.wit.life/privacy' },
]
const handleValidEmail = (email: string) => {
setEmail(email)
dispatch(actions.email.update(email))
setIsDisabled(false)
}
const handleClick = () => navigate(routes.client.subscription())
useEffect(() => {
console.log('email', email)
}, [email])
return (
<section className='page'>
<Title variant='h2' className='mt-24'>{t('weWillEmailYou')}</Title>

View File

@ -1,4 +1,4 @@
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { FormField } from '../../types'
import './styles.css'
@ -11,14 +11,16 @@ function EmailInput(props: FormField<string>): JSX.Element {
const { name, value, placeholder, onValid, onInvalid } = props
const [email, setEmail] = useState(value)
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const inputEmail = event.target.value
setEmail(inputEmail)
if (isValidEmail(inputEmail)) {
onValid(inputEmail)
setEmail(event.target.value)
}
useEffect(() => {
if (isValidEmail(email)) {
onValid(email)
} else {
onInvalid()
}
}
}, [email, onInvalid, onValid])
return (
<div className="email-input">

View File

@ -2,8 +2,11 @@ import React from 'react'
import { BrowserRouter } from 'react-router-dom'
import i18next from 'i18next'
import { I18nextProvider, initReactI18next } from 'react-i18next'
import { Provider } from 'react-redux'
import { store } from './store'
import resources from './locales'
import routes from './routes'
import { AuthProvider } from './auth'
import App from './components/App'
const init = async () => {
@ -17,9 +20,13 @@ const init = async () => {
return (
<React.StrictMode>
<I18nextProvider i18n={i18nextInstance}>
<Provider store={store}>
<BrowserRouter>
<AuthProvider>
<App />
</AuthProvider>
</BrowserRouter>
</Provider>
</I18nextProvider>
</React.StrictMode>
)

20
src/store/birthdate.ts Normal file
View File

@ -0,0 +1,20 @@
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
const birthdateSlice = createSlice({
name: 'birthdate',
initialState: '',
reducers: {
update(state, action: PayloadAction<string>) {
state = action.payload
return state
},
clear(state) {
state = ''
return state
},
},
})
export const { actions } = birthdateSlice
export default birthdateSlice.reducer

20
src/store/birthtime.ts Normal file
View File

@ -0,0 +1,20 @@
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
const birthtimeSlice = createSlice({
name: 'birthtime',
initialState: '',
reducers: {
update(state, action: PayloadAction<string>) {
state = action.payload
return state
},
clear(state) {
state = ''
return state
},
},
})
export const { actions } = birthtimeSlice
export default birthtimeSlice.reducer

20
src/store/email.ts Normal file
View File

@ -0,0 +1,20 @@
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
const emailSlice = createSlice({
name: 'email',
initialState: '',
reducers: {
update(state, action: PayloadAction<string>) {
state = action.payload
return state
},
clear(state) {
state = ''
return state
},
},
})
export const { actions } = emailSlice
export default emailSlice.reducer

22
src/store/index.ts Normal file
View File

@ -0,0 +1,22 @@
import { combineReducers, configureStore } from '@reduxjs/toolkit'
import birthdate, { actions as birthdateActions } from './birthdate'
import birthtime, { actions as birthtimeActions } from './birthtime'
import email, { actions as emailActions } from './email'
import { loadStore, backupStore } from './storageHelper'
const preloadedState = loadStore()
export const reducer = combineReducers({
email,
birthdate,
birthtime,
})
export const actions = {
email: emailActions,
birthdate: birthdateActions,
birthtime: birthtimeActions,
}
export type RootState = ReturnType<typeof reducer>
export const store = configureStore({ reducer, preloadedState })
export type AppDispatch = typeof store.dispatch
export type StoreType = typeof store
export const unsubscribe = backupStore(store)

View File

@ -0,0 +1,38 @@
import { ToolkitStore } from "@reduxjs/toolkit/dist/configureStore"
const storageKey = '_aura'
export const backupStore = (store: ToolkitStore) => {
const saveState = () => {
const state = store.getState()
try {
const serializedState = JSON.stringify(state)
localStorage.setItem(storageKey, serializedState)
} catch (err) {
// nothing to do
}
}
window.addEventListener('beforeunload', saveState)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
saveState()
}
})
return () => {
window.removeEventListener('beforeunload', saveState)
document.removeEventListener('visibilitychange', saveState)
}
}
export const loadStore = () => {
try {
const serializedState = localStorage.getItem(storageKey)
if (serializedState === null) {
return undefined
}
return JSON.parse(serializedState)
} catch (err) {
return undefined
}
}