redesign login/registration. add autologin from registration. add 'continue' register from accept acc
This commit is contained in:
103
package-lock.json
generated
103
package-lock.json
generated
@ -143,6 +143,7 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@ -2477,6 +2478,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@ -2520,6 +2522,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz",
|
||||
"integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@ -3651,6 +3654,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.6.tgz",
|
||||
"integrity": "sha512-R4DaYF3dgCQCUAkr4wW1w26GHXcf5rCmBRHVBuuvJvaGLmZdD8EjatP80Nz5JCw0KxORAzwftnHzXVnjR8HnFw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@mui/core-downloads-tracker": "^7.3.6",
|
||||
@ -4634,6 +4638,7 @@
|
||||
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
"@svgr/babel-preset": "8.1.0",
|
||||
@ -4743,6 +4748,7 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@swc/counter": "^0.1.3",
|
||||
"@swc/types": "^0.1.25"
|
||||
@ -5194,8 +5200,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
@ -5321,6 +5326,7 @@
|
||||
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
@ -5579,6 +5585,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
@ -5636,6 +5643,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@ -5646,6 +5654,7 @@
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@ -5865,6 +5874,7 @@
|
||||
"integrity": "sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.48.1",
|
||||
@ -5895,6 +5905,7 @@
|
||||
"integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.48.1",
|
||||
"@typescript-eslint/types": "8.48.1",
|
||||
@ -6800,6 +6811,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@ -6907,6 +6919,7 @@
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@ -7209,7 +7222,6 @@
|
||||
"integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"archiver-utils": "^2.1.0",
|
||||
"async": "^3.2.4",
|
||||
@ -7229,7 +7241,6 @@
|
||||
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.4",
|
||||
"graceful-fs": "^4.2.0",
|
||||
@ -7252,7 +7263,6 @@
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -7265,7 +7275,6 @@
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@ -7286,8 +7295,7 @@
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/archiver-utils/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
@ -7295,7 +7303,6 @@
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@ -7309,7 +7316,6 @@
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@ -7325,8 +7331,7 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/archiver-utils/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
@ -7334,7 +7339,6 @@
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@ -8112,6 +8116,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@ -8919,7 +8924,6 @@
|
||||
"integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"buffer-crc32": "^0.2.13",
|
||||
"crc32-stream": "^4.0.2",
|
||||
@ -9230,7 +9234,6 @@
|
||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"crc32": "bin/crc32.njs"
|
||||
},
|
||||
@ -9244,7 +9247,6 @@
|
||||
"integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"crc-32": "^1.2.0",
|
||||
"readable-stream": "^3.4.0"
|
||||
@ -10080,6 +10082,7 @@
|
||||
"integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"app-builder-lib": "25.1.8",
|
||||
"builder-util": "25.1.7",
|
||||
@ -10150,8 +10153,7 @@
|
||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dom-converter": {
|
||||
"version": "0.2.0",
|
||||
@ -10353,6 +10355,7 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@electron/get": "^2.0.0",
|
||||
"@types/node": "^22.7.7",
|
||||
@ -10958,6 +10961,7 @@
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
@ -11074,6 +11078,7 @@
|
||||
"integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "6.21.0",
|
||||
@ -11110,6 +11115,7 @@
|
||||
"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "6.21.0",
|
||||
"@typescript-eslint/types": "6.21.0",
|
||||
@ -11480,6 +11486,7 @@
|
||||
"integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.7",
|
||||
"aria-query": "^5.1.3",
|
||||
@ -11561,6 +11568,7 @@
|
||||
"integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@ -11647,6 +11655,7 @@
|
||||
"integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@ -11870,6 +11879,7 @@
|
||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
@ -12101,6 +12111,7 @@
|
||||
"integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.8",
|
||||
"array.prototype.findlast": "^1.2.5",
|
||||
@ -13071,8 +13082,7 @@
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "10.1.0",
|
||||
@ -15269,6 +15279,7 @@
|
||||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/core": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
@ -16616,7 +16627,6 @@
|
||||
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"readable-stream": "^2.0.5"
|
||||
},
|
||||
@ -16629,8 +16639,7 @@
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lazystream/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
@ -16638,7 +16647,6 @@
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@ -16654,8 +16662,7 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lazystream/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
@ -16663,7 +16670,6 @@
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@ -16784,8 +16790,7 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.difference": {
|
||||
"version": "4.5.0",
|
||||
@ -16805,8 +16810,7 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
@ -16820,8 +16824,7 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
@ -16842,8 +16845,7 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.uniq": {
|
||||
"version": "4.5.0",
|
||||
@ -16927,7 +16929,6 @@
|
||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"lz-string": "bin/bin.js"
|
||||
}
|
||||
@ -19375,6 +19376,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@ -19979,6 +19981,7 @@
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@ -20019,7 +20022,6 @@
|
||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1",
|
||||
"ansi-styles": "^5.0.0",
|
||||
@ -20035,7 +20037,6 @@
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@ -20048,8 +20049,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proc-log": {
|
||||
"version": "2.0.1",
|
||||
@ -20342,6 +20342,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
|
||||
"integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -20351,6 +20352,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
|
||||
"integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@ -20406,6 +20408,7 @@
|
||||
"integrity": "sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -20511,7 +20514,6 @@
|
||||
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimatch": "^5.1.0"
|
||||
}
|
||||
@ -20522,7 +20524,6 @@
|
||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
@ -21278,6 +21279,7 @@
|
||||
"integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
"immutable": "^5.0.2",
|
||||
@ -21385,6 +21387,7 @@
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@ -22824,7 +22827,6 @@
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
@ -23101,6 +23103,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@ -23394,6 +23397,7 @@
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
@ -23528,7 +23532,8 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD"
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tsutils": {
|
||||
"version": "3.21.0",
|
||||
@ -23582,6 +23587,7 @@
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@ -23687,6 +23693,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@ -23970,6 +23977,7 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"napi-postinstall": "^0.3.0"
|
||||
},
|
||||
@ -24350,6 +24358,7 @@
|
||||
"integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.8",
|
||||
@ -24588,6 +24597,7 @@
|
||||
"integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/bonjour": "^3.5.13",
|
||||
"@types/connect-history-api-fallback": "^1.5.4",
|
||||
@ -25193,7 +25203,6 @@
|
||||
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"archiver-utils": "^3.0.4",
|
||||
"compress-commons": "^4.1.2",
|
||||
@ -25209,7 +25218,6 @@
|
||||
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.2.3",
|
||||
"graceful-fs": "^4.2.0",
|
||||
@ -25232,7 +25240,6 @@
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -25245,7 +25252,6 @@
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@ -25267,7 +25273,6 @@
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
|
||||
@ -289,6 +289,7 @@ export default function TopUpPage() {
|
||||
navigate('/');
|
||||
}}
|
||||
sx={{
|
||||
fontFamily: 'Benzin-Bold',
|
||||
borderRadius: 999,
|
||||
px: 3,
|
||||
borderColor: 'rgba(255,255,255,0.18)',
|
||||
@ -440,11 +441,11 @@ export default function TopUpPage() {
|
||||
>
|
||||
<StyledTextField
|
||||
label="Сколько монет нужно?"
|
||||
type="number"
|
||||
value={coins}
|
||||
onChange={(e) => setCoins(Number(e.target.value))}
|
||||
inputProps={{ min: 0, step: 1 }}
|
||||
fullWidth
|
||||
sx={{'& .MuiFormLabel-root': {fontFamily: 'Benzin-Bold'}}}
|
||||
/>
|
||||
|
||||
<Box
|
||||
|
||||
@ -1,10 +1,27 @@
|
||||
import { Box, Button, Typography } from '@mui/material';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Typography,
|
||||
Paper,
|
||||
Stack,
|
||||
Divider,
|
||||
ToggleButton,
|
||||
ToggleButtonGroup,
|
||||
IconButton,
|
||||
} from '@mui/material';
|
||||
import useAuth from '../hooks/useAuth';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import PopaPopa from '../components/popa-popa';
|
||||
import useConfig from '../hooks/useConfig';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { FullScreenLoader } from '../components/FullScreenLoader';
|
||||
import { styled, alpha, keyframes } from '@mui/material/styles';
|
||||
|
||||
import TelegramIcon from '@mui/icons-material/Telegram';
|
||||
import KeyIcon from '@mui/icons-material/Key';
|
||||
import RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';
|
||||
import PersonAddAlt1RoundedIcon from '@mui/icons-material/PersonAddAlt1Rounded';
|
||||
import OpenInNewRoundedIcon from '@mui/icons-material/OpenInNewRounded';
|
||||
|
||||
import React from 'react';
|
||||
import CustomNotification from '../components/Notifications/CustomNotification';
|
||||
@ -22,10 +39,106 @@ import GradientTextField from '../components/GradientTextField';
|
||||
// твои API методы
|
||||
import { qrInit, qrStatus } from '../api';
|
||||
|
||||
import { loadPending } from '../utils/pendingVerification';
|
||||
|
||||
interface LoginProps {
|
||||
onLoginSuccess?: (username: string) => void;
|
||||
}
|
||||
|
||||
const glowPulse = keyframes`
|
||||
0% { transform: translate3d(0,0,0) scale(1); opacity: .85; filter: saturate(1.0); }
|
||||
50% { transform: translate3d(0,-6px,0) scale(1.02); opacity: 1; filter: saturate(1.15); }
|
||||
100% { transform: translate3d(0,0,0) scale(1); opacity: .85; filter: saturate(1.0); }
|
||||
`;
|
||||
|
||||
const borderShimmer = keyframes`
|
||||
0% { background-position: 0% 50%; opacity: .35; }
|
||||
50% { background-position: 100% 50%; opacity: .55; }
|
||||
100% { background-position: 0% 50%; opacity: .35; }
|
||||
`;
|
||||
|
||||
const GRADIENT =
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)';
|
||||
|
||||
const GlassPaper = styled(Paper)(() => ({
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
borderRadius: 28,
|
||||
background: 'rgba(0,0,0,0.35)',
|
||||
border: '1px solid rgba(255,255,255,0.10)',
|
||||
backdropFilter: 'blur(14px)',
|
||||
boxShadow: '0 20px 60px rgba(0,0,0,0.45)',
|
||||
}));
|
||||
|
||||
const Glow = styled('div')(() => ({
|
||||
position: 'absolute',
|
||||
inset: -2,
|
||||
background:
|
||||
'radial-gradient(800px 300px at 20% 10%, rgba(242,113,33,0.22), transparent 60%),' +
|
||||
'radial-gradient(800px 300px at 80% 0%, rgba(233,64,205,0.18), transparent 55%),' +
|
||||
'radial-gradient(900px 420px at 50% 110%, rgba(138,35,135,0.20), transparent 60%)',
|
||||
pointerEvents: 'none',
|
||||
animation: `${glowPulse} 6s ease-in-out infinite`,
|
||||
}));
|
||||
|
||||
const GradientTitle = styled(Typography)(() => ({
|
||||
fontWeight: 900,
|
||||
backgroundImage:
|
||||
'linear-gradient(136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
fontFamily: 'Benzin-Bold, sans-serif',
|
||||
}));
|
||||
|
||||
const GradientButton = styled(Button)(() => ({
|
||||
background: GRADIENT,
|
||||
fontFamily: 'Benzin-Bold, sans-serif',
|
||||
borderRadius: 999,
|
||||
textTransform: 'none',
|
||||
transition: 'transform 0.25s ease, filter 0.25s ease, box-shadow 0.25s ease',
|
||||
boxShadow: '0 12px 30px rgba(0,0,0,0.35)',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.04)',
|
||||
filter: 'brightness(1.06)',
|
||||
boxShadow: '0 16px 42px rgba(0,0,0,0.48)',
|
||||
background: GRADIENT,
|
||||
},
|
||||
'&:disabled': {
|
||||
background: 'rgba(255,255,255,0.08)',
|
||||
color: 'rgba(255,255,255,0.35)',
|
||||
boxShadow: 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
const Segmented = styled(ToggleButtonGroup)(() => ({
|
||||
borderRadius: 999,
|
||||
overflow: 'hidden',
|
||||
border: '1px solid rgba(255,255,255,0.10)',
|
||||
background: 'rgba(255,255,255,0.06)',
|
||||
'& .MuiToggleButton-root': {
|
||||
border: 'none',
|
||||
color: 'rgba(255,255,255,0.75)',
|
||||
fontFamily: 'Benzin-Bold, sans-serif',
|
||||
letterSpacing: '0.02em',
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
paddingLeft: 18,
|
||||
paddingRight: 18,
|
||||
transition: 'transform 0.2s ease, background 0.2s ease, color 0.2s ease',
|
||||
},
|
||||
'& .MuiToggleButton-root:hover': {
|
||||
background: 'rgba(255,255,255,0.08)',
|
||||
transform: 'scale(1.02)',
|
||||
},
|
||||
'& .MuiToggleButton-root.Mui-selected': {
|
||||
color: '#fff',
|
||||
background: GRADIENT,
|
||||
},
|
||||
'& .MuiToggleButton-root.Mui-selected:hover': {
|
||||
background: GRADIENT,
|
||||
},
|
||||
}));
|
||||
|
||||
const Login = ({ onLoginSuccess }: LoginProps) => {
|
||||
const navigate = useNavigate();
|
||||
const { config, saveConfig, handleInputChange } = useConfig();
|
||||
@ -40,6 +153,9 @@ const Login = ({ onLoginSuccess }: LoginProps) => {
|
||||
const [qrUrl, setQrUrl] = useState<string>('');
|
||||
const qrRef = useRef<HTMLDivElement | null>(null);
|
||||
const pollTimerRef = useRef<number | null>(null);
|
||||
const [qrState, setQrState] = useState<'idle' | 'ready' | 'polling' | 'expired'>('idle');
|
||||
|
||||
const [pendingCount, setPendingCount] = useState(0);
|
||||
|
||||
// хранит один инстанс QRCodeStyling
|
||||
const qrInstanceRef = useRef<QRCodeStyling | null>(null);
|
||||
@ -67,6 +183,24 @@ const Login = ({ onLoginSuccess }: LoginProps) => {
|
||||
horizontal: 'center',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const list = loadPending();
|
||||
setPendingCount(list.length);
|
||||
}, []);
|
||||
|
||||
const handleContinuePending = () => {
|
||||
const list = loadPending();
|
||||
if (!list.length) return;
|
||||
|
||||
const last = list[0];
|
||||
|
||||
// чтобы Registration подхватил и сразу открыл verification
|
||||
// можно также записать в launcher_config — удобно
|
||||
saveConfig({ username: last.username, password: last.password ?? '' });
|
||||
|
||||
navigate('/registration', { replace: true });
|
||||
};
|
||||
|
||||
const showNotification = (
|
||||
message: React.ReactNode,
|
||||
severity: 'success' | 'info' | 'warning' | 'error' = 'info',
|
||||
@ -145,13 +279,16 @@ const Login = ({ onLoginSuccess }: LoginProps) => {
|
||||
|
||||
const startQrLogin = async () => {
|
||||
setQrLoading(true);
|
||||
setQrState('idle');
|
||||
setQrUrl('');
|
||||
stopQrPolling();
|
||||
|
||||
try {
|
||||
const init = await qrInit(deviceId);
|
||||
setQrUrl(init.qr_url);
|
||||
setQrState('ready');
|
||||
|
||||
setQrState('polling');
|
||||
pollTimerRef.current = window.setInterval(async () => {
|
||||
try {
|
||||
const res = await qrStatus(init.token, deviceId);
|
||||
@ -178,6 +315,7 @@ const Login = ({ onLoginSuccess }: LoginProps) => {
|
||||
|
||||
if (res.status === 'expired') {
|
||||
stopQrPolling();
|
||||
setQrState('expired');
|
||||
showNotification('QR-код истёк. Нажми “Обновить QR”.', 'warning');
|
||||
}
|
||||
} catch {
|
||||
@ -320,106 +458,347 @@ const Login = ({ onLoginSuccess }: LoginProps) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1vh',
|
||||
width: '50vw',
|
||||
mx: 'auto',
|
||||
width: '100%',
|
||||
minHeight: 'calc(100vh - 8vh)',
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
px: '2vw',
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<FullScreenLoader message="Входим..." />
|
||||
) : (
|
||||
<>
|
||||
<GlassPaper
|
||||
sx={{
|
||||
// width: 'min(64vw, 980px)',
|
||||
borderRadius: '2.2vw',
|
||||
}}
|
||||
>
|
||||
<Glow />
|
||||
|
||||
{/* ===== QR экран по умолчанию ===== */}
|
||||
<Box sx={{ position: 'relative', p: '2.2vw' }}>
|
||||
{/* header */}
|
||||
<Stack alignItems="center">
|
||||
<PopaPopa />
|
||||
|
||||
{!showPasswordLogin ? (
|
||||
<Stack direction="row" spacing={1} alignItems="center" sx={{ my: '1vw' }}>
|
||||
<GradientTitle sx={{ fontSize: 'clamp(16px, 1.2vw, 18px)' }}>
|
||||
Вход через Telegram
|
||||
</GradientTitle>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
px: 1.2,
|
||||
py: 0.45,
|
||||
borderRadius: 999,
|
||||
fontSize: 'clamp(10px, 0.75vw, 12px)',
|
||||
fontWeight: 900,
|
||||
letterSpacing: '0.03em',
|
||||
color: 'rgba(255,255,255,0.9)',
|
||||
border: '1px solid rgba(255,255,255,0.10)',
|
||||
background:
|
||||
qrState === 'polling'
|
||||
? 'rgba(255,255,255,0.06)'
|
||||
: qrState === 'expired'
|
||||
? 'rgba(255,60,60,0.12)'
|
||||
: 'rgba(255,255,255,0.06)',
|
||||
}}
|
||||
>
|
||||
{qrState === 'polling' ? 'ожидание' : qrState === 'expired' ? 'истёк' : 'готов'}
|
||||
</Box>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack direction="row" spacing={1} alignItems="center" sx={{ my: '1vw' }}>
|
||||
<GradientTitle sx={{ fontSize: 'clamp(16px, 1.2vw, 18px)' }}>
|
||||
Вход по логину и паролю
|
||||
</GradientTitle>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{/* segmented */}
|
||||
{pendingCount > 0 ? (
|
||||
<Box sx={{ mb: '1vw' }}>
|
||||
{/* <Button
|
||||
fullWidth
|
||||
onClick={handleContinuePending}
|
||||
sx={{
|
||||
borderRadius: 999,
|
||||
py: 1.1,
|
||||
textTransform: 'none',
|
||||
fontFamily: 'Benzin-Bold, sans-serif',
|
||||
color: '#fff',
|
||||
border: '1px solid rgba(255,255,255,0.14)',
|
||||
background: 'rgba(255,255,255,0.06)',
|
||||
'&:hover': { background: 'rgba(255,255,255,0.09)' },
|
||||
}}
|
||||
>
|
||||
У вас {pendingCount} неподтвержденный аккаунт. Подтвердить сейчас
|
||||
</Button> */}
|
||||
<Typography>
|
||||
У вас {pendingCount} неподтвержденный аккаунт. <span onClick={handleContinuePending} style={{ cursor: 'pointer', borderBottom: '1px solid #fff'}}>Подтвердить сейчас</span>
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
<Segmented
|
||||
exclusive
|
||||
value={showPasswordLogin ? 'password' : 'qr'}
|
||||
onChange={(_, v) => {
|
||||
if (!v) return;
|
||||
if (v === 'password') goToPasswordLogin();
|
||||
else backToQr();
|
||||
}}
|
||||
sx={{ mb: '1vw' }}
|
||||
>
|
||||
<ToggleButton value="qr">
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<TelegramIcon sx={{ fontSize: 18 }} />
|
||||
<span style={{textTransform: 'none'}}>Telegram QR</span>
|
||||
</Stack>
|
||||
</ToggleButton>
|
||||
|
||||
<ToggleButton value="password">
|
||||
<Stack direction="row" spacing={1} alignItems="center">
|
||||
<KeyIcon sx={{ fontSize: 18 }} />
|
||||
<span style={{textTransform: 'none'}}>Логин + пароль</span>
|
||||
</Stack>
|
||||
</ToggleButton>
|
||||
</Segmented>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
<Divider sx={{ borderColor: 'rgba(255,255,255,0.08)', mb: '1.6vw' }} />
|
||||
|
||||
{/* content */}
|
||||
{!showPasswordLogin ? (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1,
|
||||
alignItems: 'center',
|
||||
display: 'grid',
|
||||
gridTemplateColumns: { xs: '1fr', md: '1.05fr 0.95fr' },
|
||||
gap: '1.6vw',
|
||||
alignItems: 'start',
|
||||
}}
|
||||
>
|
||||
<PopaPopa/>
|
||||
<Typography
|
||||
{/* QR card */}
|
||||
<Box
|
||||
sx={{
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '2vw',
|
||||
backgroundImage:
|
||||
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
position: 'relative',
|
||||
borderRadius: '1.6vw',
|
||||
border: '1px solid rgba(255,255,255,0.10)',
|
||||
background:
|
||||
'linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.03))',
|
||||
p: '1.2vw',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
Вход через Telegram
|
||||
</Typography>
|
||||
{/* subtle top glow like marketplace cards */}
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
pointerEvents: 'none',
|
||||
background:
|
||||
'radial-gradient(circle at top, rgba(242,113,33,0.18), transparent 60%)',
|
||||
opacity: 0.9,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={primaryButtonSx}
|
||||
onClick={() => qrUrl && window.open(qrUrl, '_blank')}
|
||||
disabled={!qrUrl}
|
||||
<Box sx={{ position: 'relative' }}>
|
||||
{/* IMPORTANT: relative wrapper so expired overlay is positioned correctly */}
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
borderRadius: '1.2vw',
|
||||
border: '1px solid rgba(255,255,255,0.12)',
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(40,40,40,0.55), rgba(15,15,15,0.55))',
|
||||
// minHeight: 340,
|
||||
// py: 2,
|
||||
boxShadow: 'inset 0 0 0 1px rgba(255,255,255,0.04)',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
Открыть бота
|
||||
</Button>
|
||||
|
||||
{/* QR контейнер */}
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
inset: -2,
|
||||
borderRadius: '1.3vw',
|
||||
padding: '2px',
|
||||
background:
|
||||
'linear-gradient(90deg, rgba(242,113,33,0.0), rgba(242,113,33,0.35), rgba(233,64,205,0.35), rgba(138,35,135,0.35), rgba(242,113,33,0.0))',
|
||||
backgroundSize: '240% 240%',
|
||||
animation: `${borderShimmer} 7s ease-in-out infinite`,
|
||||
pointerEvents: 'none',
|
||||
mask:
|
||||
'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)',
|
||||
WebkitMask:
|
||||
'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)',
|
||||
maskComposite: 'exclude',
|
||||
WebkitMaskComposite: 'xor',
|
||||
opacity: qrState === 'expired' ? 0.18 : 0.45,
|
||||
}}
|
||||
/>
|
||||
<div ref={qrRef} style={{ minHeight: 300 }} />
|
||||
|
||||
{qrState === 'expired' && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
gap: '2vh',
|
||||
}}
|
||||
>
|
||||
{qrLoading && <FullScreenLoader fullScreen={true} /> }
|
||||
<Typography onClick={startQrLogin} sx={gradientTextSx}>
|
||||
Обновить QR
|
||||
</Typography>
|
||||
<Typography onClick={goToPasswordLogin} sx={gradientTextSx}>
|
||||
Войти по логину и паролю
|
||||
</Typography>
|
||||
<Typography
|
||||
onClick={() => navigate('/registration')}
|
||||
sx={gradientTextSx}
|
||||
>
|
||||
Зарегистрироваться
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
/* ===== экран логин/пароль (в стиле Registration.tsx) ===== */
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
display: 'grid',
|
||||
placeItems: 'center',
|
||||
background:
|
||||
'linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.35))',
|
||||
backdropFilter: 'blur(10px)',
|
||||
textAlign: 'center',
|
||||
borderRadius: '1.2vw',
|
||||
px: 2,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '2vw',
|
||||
backgroundImage:
|
||||
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
fontSize: 'clamp(14px, 1.05vw, 18px)',
|
||||
color: alpha('#fff', 0.92),
|
||||
}}
|
||||
>
|
||||
Вход по логину и паролю
|
||||
QR-код истёк
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
mt: 0.6,
|
||||
fontSize: 'clamp(12px, 0.9vw, 14px)',
|
||||
color: alpha('#fff', 0.75),
|
||||
}}
|
||||
>
|
||||
Нажми “Обновить QR”, чтобы получить новый
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
mt: 1,
|
||||
textAlign: 'center',
|
||||
fontSize: 'clamp(12px, 0.9vw, 14px)',
|
||||
color: alpha('#fff', 0.75),
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{qrState === 'polling' && 'Ожидаем подтверждение…'}
|
||||
{qrState === 'ready' && 'Сканируй QR в Telegram'}
|
||||
{qrState === 'expired' && 'Нужно обновить QR'}
|
||||
{qrState === 'idle' && 'Подготавливаем вход…'}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* actions */}
|
||||
<Stack spacing={1.2} sx={{ pt: { xs: 0, md: '0.4vw' } }}>
|
||||
<GradientTitle sx={{ fontSize: 'clamp(16px, 1.2vw, 18px)' }}>
|
||||
Вход через Telegram
|
||||
</GradientTitle>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
color: 'rgba(255,255,255,0.70)',
|
||||
fontWeight: 700,
|
||||
fontSize: 'clamp(12px, 0.9vw, 14px)',
|
||||
lineHeight: 1.35,
|
||||
}}
|
||||
>
|
||||
1) Открой бота <br />
|
||||
2) Сканируй QR <br />
|
||||
3) Подтверди вход
|
||||
</Typography>
|
||||
|
||||
<GradientButton
|
||||
variant="contained"
|
||||
onClick={() => qrUrl && window.open(qrUrl, '_blank')}
|
||||
disabled={!qrUrl}
|
||||
startIcon={<OpenInNewRoundedIcon />}
|
||||
sx={{ py: 1.2, fontSize: 'clamp(12px, 0.95vw, 14px)' }}
|
||||
>
|
||||
Открыть бота
|
||||
</GradientButton>
|
||||
|
||||
<Button
|
||||
disableRipple
|
||||
disableFocusRipple
|
||||
onClick={startQrLogin}
|
||||
startIcon={<RefreshRoundedIcon />}
|
||||
sx={{
|
||||
borderRadius: 999,
|
||||
textTransform: 'none',
|
||||
py: 1.0,
|
||||
fontFamily: 'Benzin-Bold',
|
||||
color: '#fff',
|
||||
border: '1px solid rgba(255,255,255,0.12)',
|
||||
background: 'rgba(255,255,255,0.06)',
|
||||
'&:hover': { background: 'rgba(255,255,255,0.08)' },
|
||||
}}
|
||||
>
|
||||
Обновить QR
|
||||
</Button>
|
||||
|
||||
<Divider sx={{ borderColor: 'rgba(255,255,255,0.08)', my: 0.6 }} />
|
||||
|
||||
<Button
|
||||
disableRipple
|
||||
disableFocusRipple
|
||||
onClick={() => navigate('/registration')}
|
||||
startIcon={<PersonAddAlt1RoundedIcon />}
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
borderRadius: 999,
|
||||
py: 1.0,
|
||||
fontFamily: 'Benzin-Bold',
|
||||
color: '#fff',
|
||||
background: 'rgba(255,255,255,0.06)',
|
||||
'&:hover': { background: 'rgba(255,255,255,0.08)' },
|
||||
}}
|
||||
>
|
||||
Зарегистрироваться
|
||||
</Button>
|
||||
|
||||
{qrLoading && <FullScreenLoader fullScreen={false} message="Генерируем QR..." />}
|
||||
</Stack>
|
||||
</Box>
|
||||
) : (
|
||||
/* password */
|
||||
<Box sx={{ display: 'grid', placeItems: 'center' }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 'min(520px, 100%)',
|
||||
borderRadius: '1.6vw',
|
||||
border: '1px solid rgba(255,255,255,0.10)',
|
||||
background:
|
||||
'linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.03))',
|
||||
p: '1.6vw',
|
||||
}}
|
||||
>
|
||||
<GradientTitle sx={{ fontSize: 'clamp(16px, 1.2vw, 18px)', mb: 1 }}>
|
||||
Вход по логину и паролю
|
||||
</GradientTitle>
|
||||
|
||||
<Stack spacing={1.2}>
|
||||
<GradientTextField
|
||||
label="Никнейм"
|
||||
required
|
||||
name="username"
|
||||
value={config.username}
|
||||
onChange={handleInputChange}
|
||||
sx={{
|
||||
'& .MuiInputLabel-root.Mui-focused': {
|
||||
display: 'none',
|
||||
},
|
||||
'& .MuiInputLabel-root.MuiInputLabel-shrink': {
|
||||
display: 'none',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<GradientTextField
|
||||
@ -429,38 +808,71 @@ const Login = ({ onLoginSuccess }: LoginProps) => {
|
||||
type="password"
|
||||
value={config.password}
|
||||
onChange={handleInputChange}
|
||||
sx={{
|
||||
'& .MuiInputLabel-root.Mui-focused': {
|
||||
display: 'none',
|
||||
},
|
||||
'& .MuiInputLabel-root.MuiInputLabel-shrink': {
|
||||
display: 'none',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
<GradientButton
|
||||
variant="contained"
|
||||
sx={primaryButtonSx}
|
||||
onClick={handleLogin}
|
||||
>
|
||||
Войти
|
||||
</Button>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
gap: '2vh',
|
||||
mt: 2,
|
||||
py: 1.2,
|
||||
fontSize: 'clamp(12px, 0.95vw, 14px)',
|
||||
mt: 0.6,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
Войти
|
||||
</GradientButton>
|
||||
|
||||
<Stack direction="row" spacing={1}>
|
||||
<Button
|
||||
fullWidth
|
||||
disableRipple
|
||||
disableFocusRipple
|
||||
onClick={() => navigate('/registration')}
|
||||
sx={gradientTextSx}
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
borderRadius: 999,
|
||||
py: 1.0,
|
||||
fontFamily: 'Benzin-Bold',
|
||||
color: '#fff',
|
||||
background: 'rgba(255,255,255,0.06)',
|
||||
'&:hover': { background: 'rgba(255,255,255,0.08)' },
|
||||
}}
|
||||
>
|
||||
Регистрация
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
fullWidth
|
||||
disableRipple
|
||||
disableFocusRipple
|
||||
onClick={backToQr}
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
borderRadius: 999,
|
||||
py: 1.0,
|
||||
fontFamily: 'Benzin-Bold',
|
||||
color: '#fff',
|
||||
background: 'rgba(255,255,255,0.06)',
|
||||
'&:hover': { background: 'rgba(255,255,255,0.08)' },
|
||||
}}
|
||||
>
|
||||
Зарегистрироваться
|
||||
</Typography>
|
||||
<Typography onClick={backToQr} sx={gradientTextSx}>
|
||||
Назад к QR
|
||||
</Typography>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
</Box>
|
||||
</GlassPaper>
|
||||
)}
|
||||
|
||||
<CustomNotification
|
||||
|
||||
@ -223,20 +223,14 @@ export default function Marketplace() {
|
||||
|
||||
const parsed = Number(editPriceValue);
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) {
|
||||
showNotification('Цена должна быть числом больше 0', 'warning', {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
});
|
||||
showNotification('Цена должна быть числом больше 0', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateMarketplaceItemPrice(username, editItem.id, parsed);
|
||||
|
||||
showNotification('Цена обновлена', 'success', {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
});
|
||||
showNotification('Цена обновлена', 'success');
|
||||
|
||||
setEditPriceOpen(false);
|
||||
setEditItem(null);
|
||||
@ -248,7 +242,6 @@ export default function Marketplace() {
|
||||
showNotification(
|
||||
e instanceof Error ? e.message : 'Ошибка при изменении цены',
|
||||
'error',
|
||||
{ vertical: 'bottom', horizontal: 'left' },
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -256,13 +249,18 @@ export default function Marketplace() {
|
||||
const handleRemoveItem = async () => {
|
||||
if (!username || !selectedServer || !removeItem) return;
|
||||
|
||||
if (!canBuyOnSelectedServer) {
|
||||
showNotification(
|
||||
'Для снятия товара с продажи нужно быть на сервере.',
|
||||
'error',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await cancelMarketplaceItemSale(username, removeItem.id);
|
||||
|
||||
showNotification('Товар снят с продажи', 'success', {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
});
|
||||
showNotification('Товар снят с продажи', 'success');
|
||||
|
||||
setRemoveOpen(false);
|
||||
setRemoveItem(null);
|
||||
@ -274,7 +272,6 @@ export default function Marketplace() {
|
||||
showNotification(
|
||||
e instanceof Error ? e.message : 'Ошибка при снятии товара',
|
||||
'error',
|
||||
{ vertical: 'bottom', horizontal: 'left' },
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -332,7 +329,6 @@ export default function Marketplace() {
|
||||
showNotification(
|
||||
'Чтобы покупать предметы, нужно находиться на сервере игры.',
|
||||
'warning',
|
||||
{ vertical: 'bottom', horizontal: 'left' },
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -346,7 +342,6 @@ export default function Marketplace() {
|
||||
<b>{translateServer(selectedServer?.name ?? '')}</b>.
|
||||
</>,
|
||||
'info',
|
||||
{ vertical: 'bottom', horizontal: 'left' },
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -359,7 +354,6 @@ export default function Marketplace() {
|
||||
showNotification(
|
||||
result.message || 'Предмет успешно куплен! Он будет добавлен в ваш инвентарь.',
|
||||
'success',
|
||||
{ vertical: 'bottom', horizontal: 'left' },
|
||||
);
|
||||
|
||||
// обновляем список предметов
|
||||
@ -369,7 +363,6 @@ export default function Marketplace() {
|
||||
showNotification(
|
||||
error instanceof Error ? error.message : 'Ошибка при покупке предмета',
|
||||
'error',
|
||||
{ vertical: 'bottom', horizontal: 'left' },
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -1167,10 +1160,7 @@ export default function Marketplace() {
|
||||
serverIp={playerServer.ip}
|
||||
onSellSuccess={() => {
|
||||
if (selectedServer) loadMarketItems(selectedServer.ip, 1);
|
||||
showNotification('Предмет успешно выставлен на продажу!', 'success', {
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
});
|
||||
showNotification('Предмет успешно выставлен на продажу!', 'success');
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -61,6 +61,9 @@ function getRarityColor(weight?: number): string {
|
||||
}
|
||||
}
|
||||
|
||||
const GRADIENT =
|
||||
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)';
|
||||
|
||||
export default function Shop() {
|
||||
const [storeCapes, setStoreCapes] = useState<StoreCape[]>([]);
|
||||
const [userCapes, setUserCapes] = useState<Cape[]>([]);
|
||||
@ -120,6 +123,18 @@ export default function Shop() {
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
const showNotification = (
|
||||
message: React.ReactNode,
|
||||
severity: 'success' | 'info' | 'warning' | 'error' = 'info',
|
||||
position: NotificationPosition = getNotifPositionFromSettings(),
|
||||
) => {
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg(message);
|
||||
setNotifSeverity(severity);
|
||||
setNotifPos(position);
|
||||
setNotifOpen(true);
|
||||
};
|
||||
|
||||
const loadBonuses = async (username: string) => {
|
||||
try {
|
||||
setBonusesLoading(true);
|
||||
@ -133,10 +148,7 @@ export default function Shop() {
|
||||
console.error('Ошибка при получении прокачек:', error);
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Ошибка при загрузке прокачки!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Ошибка при загрузке прокачки!', 'error')
|
||||
} finally {
|
||||
setBonusesLoading(false);
|
||||
}
|
||||
@ -182,18 +194,12 @@ export default function Shop() {
|
||||
playBuySound();
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Плащ успешно куплен!');
|
||||
setNotifSeverity('success');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Плащ успешно куплен!', 'success')
|
||||
} catch (error) {
|
||||
console.error('Ошибка при покупке плаща:', error);
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Ошибка при покупке плаща!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Ошибка при покупке плаща!', 'error')
|
||||
}
|
||||
};
|
||||
|
||||
@ -275,10 +281,7 @@ export default function Shop() {
|
||||
if (!username) {
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Не найдено имя игрока. Авторизируйтесь в лаунчере!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Не найдено имя игрока. Авторизируйтесь в лаунчере!', 'error')
|
||||
return;
|
||||
}
|
||||
|
||||
@ -291,18 +294,12 @@ export default function Shop() {
|
||||
await loadBonuses(username);
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Прокачка успешно куплена!');
|
||||
setNotifSeverity('success');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Прокачка успешно куплена!', 'success')
|
||||
} catch (error) {
|
||||
console.error('Ошибка при покупке прокачки:', error);
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Ошибка при прокачке!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Ошибка при прокачке!', 'error')
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -317,18 +314,12 @@ export default function Shop() {
|
||||
await loadBonuses(username);
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Бонус улучшен!');
|
||||
setNotifSeverity('success');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Бонус улучшен!', 'success')
|
||||
} catch (error) {
|
||||
console.error('Ошибка при улучшении бонуса:', error);
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Ошибка при улучшении бонуса!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Ошибка при улучшении бонуса!', 'error')
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -343,10 +334,7 @@ export default function Shop() {
|
||||
} catch (error) {
|
||||
console.error('Ошибка при переключении бонуса:', error);
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Ошибка при переключении бонуса!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Ошибка при переключении бонуса!', 'error')
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -389,33 +377,22 @@ const filteredCases = (cases || []).filter((c) => {
|
||||
const handleOpenCase = async (caseData: Case) => {
|
||||
if (!username) {
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Не найдено имя игрока. Авторизуйтесь в лаунчере!');
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Не найдено имя игрока. Авторизуйтесь в лаунчере!', 'error')
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedCaseServerIp) {
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Выберите сервер для открытия кейса!');
|
||||
setNotifSeverity('warning');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Выберите сервер для открытия кейса!', 'warning')
|
||||
return;
|
||||
}
|
||||
|
||||
const allowedIps = caseData.server_ips || [];
|
||||
if (allowedIps.length > 0 && !allowedIps.includes(selectedCaseServerIp)) {
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg(
|
||||
`Этот кейс доступен на: ${allowedIps
|
||||
showNotification(`Этот кейс доступен на: ${allowedIps
|
||||
.map((ip) => translateServer(`Server ${ip}`))
|
||||
.join(', ')}`,
|
||||
);
|
||||
setNotifSeverity('warning');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
.join(', ')}`, 'warning')
|
||||
return;
|
||||
}
|
||||
|
||||
@ -437,18 +414,12 @@ const filteredCases = (cases || []).filter((c) => {
|
||||
playBuySound();
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg('Кейс открыт!');
|
||||
setNotifSeverity('success');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification('Кейс открыт!', 'success')
|
||||
} catch (error) {
|
||||
console.error('Ошибка при открытии кейса:', error);
|
||||
|
||||
if (!isNotificationsEnabled()) return;
|
||||
setNotifMsg(String(error instanceof Error ? error.message : 'Ошибка при открытии кейса!'));
|
||||
setNotifSeverity('error');
|
||||
setNotifPos({ vertical: 'top', horizontal: 'center' });
|
||||
setNotifOpen(true);
|
||||
showNotification((String(error instanceof Error ? error.message : 'Ошибка при открытии кейса!')), 'error')
|
||||
} finally {
|
||||
setIsOpening(false);
|
||||
}
|
||||
@ -617,10 +588,78 @@ const filteredCases = (cases || []).filter((c) => {
|
||||
</Typography>
|
||||
|
||||
{caseServers.length > 0 && (
|
||||
<FormControl size="small" sx={{ minWidth: 220 }}>
|
||||
<InputLabel id="cases-server-label" sx={{ fontFamily: 'Benzin-Bold', color: 'rgba(255,255,255,0.75)' }}>
|
||||
Сервер
|
||||
</InputLabel>
|
||||
<FormControl
|
||||
size="small"
|
||||
sx={{
|
||||
// textTransform: 'uppercase',
|
||||
// чтобы по ширине вел себя как бейдж
|
||||
// minWidth: '18vw',
|
||||
// maxWidth: '28vw',
|
||||
|
||||
'& .MuiInputLabel-root': { display: 'none' },
|
||||
|
||||
'& .MuiOutlinedInput-root': {
|
||||
borderRadius: '3vw',
|
||||
position: 'relative',
|
||||
px: '1.2vw',
|
||||
py: '0.5vw',
|
||||
|
||||
color: 'rgba(255,255,255,0.95)',
|
||||
background:
|
||||
'radial-gradient(circle at 10% 10%, rgba(242,113,33,0.20), transparent 55%), radial-gradient(circle at 90% 20%, rgba(233,64,205,0.16), transparent 55%), rgba(10,10,20,0.92)',
|
||||
border: '1px solid rgba(255,255,255,0.10)',
|
||||
boxShadow: '0 1.4vw 3.8vw rgba(0,0,0,0.55)',
|
||||
backdropFilter: 'blur(14px)',
|
||||
overflow: 'hidden',
|
||||
|
||||
transition: 'transform 0.18s ease, filter 0.18s ease',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.02)',
|
||||
// filter: 'brightness(1.03)',
|
||||
},
|
||||
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
border: 'none',
|
||||
},
|
||||
|
||||
// нижняя градиентная полоска как у username
|
||||
'&:after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
left: '0%',
|
||||
right: '0%',
|
||||
bottom: 0,
|
||||
height: '0.15vw',
|
||||
borderRadius: '999px',
|
||||
background: GRADIENT,
|
||||
opacity: 0.9,
|
||||
pointerEvents: 'none',
|
||||
width: '100%',
|
||||
},
|
||||
},
|
||||
|
||||
'& .MuiSelect-select': {
|
||||
// стиль текста как у плашки username
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '1.9vw',
|
||||
lineHeight: 1.1,
|
||||
textAlign: 'center',
|
||||
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
|
||||
// убираем “инпутный” паддинг
|
||||
padding: 0,
|
||||
minHeight: 'unset',
|
||||
|
||||
// чтобы длинные названия не ломали
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Select
|
||||
labelId="cases-server-label"
|
||||
label="Сервер"
|
||||
@ -629,16 +668,19 @@ const filteredCases = (cases || []).filter((c) => {
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
sx: {
|
||||
mt: 1,
|
||||
bgcolor: 'rgba(10,10,20,0.96)',
|
||||
border: '1px solid rgba(255,255,255,0.10)',
|
||||
borderRadius: '1vw',
|
||||
backdropFilter: 'blur(14px)',
|
||||
|
||||
'& .MuiMenuItem-root': {
|
||||
color: 'rgba(255,255,255,0.9)',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
fontSize: '1.1vw',
|
||||
},
|
||||
'& .MuiMenuItem-root.Mui-selected': {
|
||||
backgroundColor: 'rgba(242,113,33,0.16)',
|
||||
backgroundColor: 'rgba(242,113,33,0.18)',
|
||||
},
|
||||
'& .MuiMenuItem-root:hover': {
|
||||
backgroundColor: 'rgba(233,64,205,0.14)',
|
||||
@ -646,29 +688,6 @@ const filteredCases = (cases || []).filter((c) => {
|
||||
},
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
borderRadius: '999px',
|
||||
bgcolor: 'rgba(255,255,255,0.04)',
|
||||
color: 'rgba(255,255,255,0.92)',
|
||||
fontFamily: 'Benzin-Bold',
|
||||
'& .MuiSelect-select': {
|
||||
py: '0.9vw',
|
||||
px: '1.2vw',
|
||||
},
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: 'rgba(255,255,255,0.14)',
|
||||
},
|
||||
'&:hover .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: 'rgba(242,113,33,0.55)',
|
||||
},
|
||||
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: 'rgba(233,64,205,0.65)',
|
||||
borderWidth: '2px',
|
||||
},
|
||||
'& .MuiSelect-icon': {
|
||||
color: 'rgba(255,255,255,0.75)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{caseServers.map((ip) => (
|
||||
<MenuItem key={ip} value={ip}>
|
||||
@ -677,6 +696,67 @@ const filteredCases = (cases || []).filter((c) => {
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
// <FormControl size="small" sx={{ minWidth: 220 }}>
|
||||
// <InputLabel id="cases-server-label" sx={{ fontFamily: 'Benzin-Bold', color: 'rgba(255,255,255,0.75)' }}>
|
||||
// Сервер
|
||||
// </InputLabel>
|
||||
// <Select
|
||||
// labelId="cases-server-label"
|
||||
// label="Сервер"
|
||||
// value={selectedCaseServerIp}
|
||||
// onChange={(e) => setSelectedCaseServerIp(String(e.target.value))}
|
||||
// MenuProps={{
|
||||
// PaperProps: {
|
||||
// sx: {
|
||||
// bgcolor: 'rgba(10,10,20,0.96)',
|
||||
// border: '1px solid rgba(255,255,255,0.10)',
|
||||
// borderRadius: '1vw',
|
||||
// backdropFilter: 'blur(14px)',
|
||||
// '& .MuiMenuItem-root': {
|
||||
// color: 'rgba(255,255,255,0.9)',
|
||||
// fontFamily: 'Benzin-Bold',
|
||||
// },
|
||||
// '& .MuiMenuItem-root.Mui-selected': {
|
||||
// backgroundColor: 'rgba(242,113,33,0.16)',
|
||||
// },
|
||||
// '& .MuiMenuItem-root:hover': {
|
||||
// backgroundColor: 'rgba(233,64,205,0.14)',
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }}
|
||||
// sx={{
|
||||
// borderRadius: '999px',
|
||||
// bgcolor: 'rgba(255,255,255,0.04)',
|
||||
// color: 'rgba(255,255,255,0.92)',
|
||||
// fontFamily: 'Benzin-Bold',
|
||||
// '& .MuiSelect-select': {
|
||||
// py: '0.9vw',
|
||||
// px: '1.2vw',
|
||||
// },
|
||||
// '& .MuiOutlinedInput-notchedOutline': {
|
||||
// borderColor: 'rgba(255,255,255,0.14)',
|
||||
// },
|
||||
// '&:hover .MuiOutlinedInput-notchedOutline': {
|
||||
// borderColor: 'rgba(242,113,33,0.55)',
|
||||
// },
|
||||
// '&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||
// borderColor: 'rgba(233,64,205,0.65)',
|
||||
// borderWidth: '2px',
|
||||
// },
|
||||
// '& .MuiSelect-icon': {
|
||||
// color: 'rgba(255,255,255,0.75)',
|
||||
// },
|
||||
// }}
|
||||
// >
|
||||
// {caseServers.map((ip) => (
|
||||
// <MenuItem key={ip} value={ip}>
|
||||
// {translateServer(`Server ${ip}`)}
|
||||
// </MenuItem>
|
||||
// ))}
|
||||
// </Select>
|
||||
// </FormControl>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
|
||||
43
src/renderer/utils/pendingVerification.ts
Normal file
43
src/renderer/utils/pendingVerification.ts
Normal file
@ -0,0 +1,43 @@
|
||||
export type PendingVerification = {
|
||||
username: string;
|
||||
password?: string;
|
||||
createdAt: number;
|
||||
};
|
||||
|
||||
const PENDING_KEY = 'pending_verifications_v1';
|
||||
|
||||
export const loadPending = (): PendingVerification[] => {
|
||||
try {
|
||||
const raw = localStorage.getItem(PENDING_KEY);
|
||||
return raw ? (JSON.parse(raw) as PendingVerification[]) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const savePending = (items: PendingVerification[]) => {
|
||||
localStorage.setItem(PENDING_KEY, JSON.stringify(items));
|
||||
};
|
||||
|
||||
export const upsertPending = (item: PendingVerification) => {
|
||||
const list = loadPending();
|
||||
const next = [
|
||||
item,
|
||||
...list.filter(
|
||||
(x) => x.username.toLowerCase() !== item.username.toLowerCase(),
|
||||
),
|
||||
].slice(0, 5);
|
||||
|
||||
savePending(next);
|
||||
};
|
||||
|
||||
export const removePending = (username: string) => {
|
||||
const list = loadPending();
|
||||
savePending(
|
||||
list.filter((x) => x.username.toLowerCase() !== username.toLowerCase()),
|
||||
);
|
||||
};
|
||||
|
||||
export const clearPending = () => {
|
||||
localStorage.removeItem(PENDING_KEY);
|
||||
};
|
||||
Reference in New Issue
Block a user