mirror of
https://github.com/Nezumi-2711/typescript-training.git
synced 2026-09-22 13:48:29 +00:00
Add login view and controller
This commit is contained in:
Generated
+310
@@ -9,6 +9,8 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/core-js": "^2.5.6",
|
||||
"@types/regenerator-runtime": "^0.13.2",
|
||||
"firebase": "^10.4.0",
|
||||
"parcel": "^2.9.3"
|
||||
},
|
||||
@@ -23,6 +25,7 @@
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"prettier": "^3.0.3",
|
||||
"process": "^0.11.10",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
},
|
||||
@@ -2731,6 +2734,11 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/core-js": {
|
||||
"version": "2.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/core-js/-/core-js-2.5.6.tgz",
|
||||
"integrity": "sha512-zLzoC7avO4EYUUYCSzDaahSP1QJEpZQcPxqs91mPeFdh2NS4hQBcnRoEc9RuXfJ8cdN/KXUWukMmZGcKaWeOvw=="
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
|
||||
@@ -2748,6 +2756,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.7.1.tgz",
|
||||
"integrity": "sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg=="
|
||||
},
|
||||
"node_modules/@types/regenerator-runtime": {
|
||||
"version": "0.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
|
||||
"integrity": "sha512-SIYCe4xgq4FSs00vENNHC+L1jxRdN4IKV2abqTu2kb2WkXlPBOml7NK2eCcYdUbQtD8cOWTcBUiYstoiaSNawA=="
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz",
|
||||
@@ -3168,6 +3181,15 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/big-integer": {
|
||||
"version": "1.6.51",
|
||||
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
|
||||
"integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
@@ -3246,6 +3268,21 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/bundle-name": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz",
|
||||
"integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"run-applescript": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
@@ -4458,6 +4495,12 @@
|
||||
"is-callable": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@@ -4914,6 +4957,18 @@
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
|
||||
},
|
||||
"node_modules/is-bigint": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
|
||||
"integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-bigints": "^1.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
@@ -4926,6 +4981,22 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-boolean-object": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
|
||||
"integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-callable": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||
@@ -5050,6 +5121,21 @@
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number-object": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
|
||||
"integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-path-inside": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
|
||||
@@ -5087,6 +5173,18 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
|
||||
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-string": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
|
||||
@@ -5171,6 +5269,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -5531,6 +5641,12 @@
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@@ -5552,6 +5668,18 @@
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-fn": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
|
||||
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
@@ -5673,6 +5801,33 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-path": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
|
||||
"integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"path-key": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-path/node_modules/path-key": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
|
||||
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/nth-check": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||
@@ -5785,6 +5940,30 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/onetime": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
|
||||
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mimic-fn": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/open": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
|
||||
@@ -5923,6 +6102,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
@@ -6048,6 +6236,15 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.2.5",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz",
|
||||
@@ -6205,6 +6402,110 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz",
|
||||
"integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"execa": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript/node_modules/execa": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"get-stream": "^6.0.0",
|
||||
"human-signals": "^2.1.0",
|
||||
"is-stream": "^2.0.0",
|
||||
"merge-stream": "^2.0.0",
|
||||
"npm-run-path": "^4.0.1",
|
||||
"onetime": "^5.1.2",
|
||||
"signal-exit": "^3.0.3",
|
||||
"strip-final-newline": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript/node_modules/human-signals": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript/node_modules/is-stream": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript/node_modules/mimic-fn": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript/node_modules/npm-run-path": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"path-key": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript/node_modules/onetime": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mimic-fn": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/run-applescript/node_modules/strip-final-newline": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
|
||||
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -6781,6 +7082,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/untildify": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
|
||||
"integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/core-js": "^2.5.6",
|
||||
"@types/regenerator-runtime": "^0.13.2",
|
||||
"firebase": "^10.4.0",
|
||||
"parcel": "^2.9.3"
|
||||
},
|
||||
@@ -24,6 +26,7 @@
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"prettier": "^3.0.3",
|
||||
"process": "^0.11.10",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,5 +57,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script type="module" src="../ts/index.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,31 +13,15 @@
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.check:before {
|
||||
top: 16px;
|
||||
left: 15px;
|
||||
margin: -5px 0 0 -6px;
|
||||
height: 10px;
|
||||
width: 20px;
|
||||
border: solid #39ca74;
|
||||
border-width: 0 0 3px 3px;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
-o-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.error {
|
||||
&:before {
|
||||
top: 6px;
|
||||
left: 13px;
|
||||
&.success:before {
|
||||
top: 16px;
|
||||
left: 15px;
|
||||
margin: -5px 0 0 -6px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border: solid #c0392b;
|
||||
border-width: 0 0 4px 4px;
|
||||
width: 20px;
|
||||
border: solid #39ca74;
|
||||
border-width: 0 0 3px 3px;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
@@ -45,17 +29,33 @@
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
&:after {
|
||||
top: 21px;
|
||||
left: 13px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border: solid #c0392b;
|
||||
border-width: 0 0 4px 4px;
|
||||
-webkit-transform: rotate(135deg);
|
||||
-moz-transform: rotate(135deg);
|
||||
-ms-transform: rotate(135deg);
|
||||
-o-transform: rotate(135deg);
|
||||
transform: rotate(135deg);
|
||||
&.error {
|
||||
&:before {
|
||||
top: 6px;
|
||||
left: 13px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border: solid #c0392b;
|
||||
border-width: 0 0 4px 4px;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
-o-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
&:after {
|
||||
top: 21px;
|
||||
left: 13px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border: solid #c0392b;
|
||||
border-width: 0 0 4px 4px;
|
||||
-webkit-transform: rotate(135deg);
|
||||
-moz-transform: rotate(135deg);
|
||||
-ms-transform: rotate(135deg);
|
||||
-o-transform: rotate(135deg);
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import Controller from './controllers/index';
|
||||
import Service from './services/index';
|
||||
import View from './views/index';
|
||||
|
||||
export default class App {
|
||||
private controller: Controller;
|
||||
|
||||
constructor() {
|
||||
this.controller = new Controller(new Service(), new View());
|
||||
}
|
||||
|
||||
start() {
|
||||
this.controller.registerController.init();
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,12 @@ export const LOCAL_STORAGE = {
|
||||
ACCESS_TOKEN: 'accessToken',
|
||||
};
|
||||
|
||||
export enum TYPE_TOAST {
|
||||
export enum TypeToast {
|
||||
success = 'success',
|
||||
error = 'error',
|
||||
}
|
||||
export enum MARK_ICON {
|
||||
success = 'check',
|
||||
export enum MarkIcon {
|
||||
success = 'success',
|
||||
error = 'error',
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import Service from 'services';
|
||||
import RegisterController from './registerController';
|
||||
import View from 'views';
|
||||
|
||||
export default class Controller {
|
||||
public registerController: RegisterController;
|
||||
|
||||
constructor(
|
||||
public service: Service,
|
||||
public view: View,
|
||||
) {
|
||||
this.registerController = new RegisterController(service, view);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import User from 'models/user';
|
||||
import Service from 'services';
|
||||
import View from 'views';
|
||||
import RegisterView from 'views/registerView';
|
||||
|
||||
export default class RegisterController {
|
||||
public registerView: RegisterView;
|
||||
|
||||
constructor(
|
||||
public service: Service,
|
||||
public view: View,
|
||||
) {
|
||||
this.registerView = view.registerView;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
handlerCheckUserValid(email: string): Promise<boolean> {
|
||||
return this.service.userService.isValidUser(email);
|
||||
}
|
||||
|
||||
handlerSaveUser(user: User): Promise<void> {
|
||||
return this.service.userService.saveUser(user);
|
||||
}
|
||||
|
||||
handlerGetInfoUserLogin(): Promise<User | null> {
|
||||
return this.service.userService.getInfoUserLogin();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this.registerView) {
|
||||
this.registerView.loadPage(this.handlerGetInfoUserLogin.bind(this));
|
||||
this.registerView.addHandlerForm(
|
||||
this.handlerCheckUserValid.bind(this),
|
||||
this.handlerSaveUser.bind(this),
|
||||
);
|
||||
this.registerView.addHandlerInputFormChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,3 +13,8 @@ export class DataObject<T> {
|
||||
this.data = dataObject.data as T;
|
||||
}
|
||||
}
|
||||
|
||||
export type TError = {
|
||||
title: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
console.log('Test');
|
||||
import App from './app';
|
||||
|
||||
// Sure that scripts called after DOM loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const myApp = new App();
|
||||
|
||||
myApp.start();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createIdUser } from '../helpers/data';
|
||||
|
||||
export default class User {
|
||||
private id: number;
|
||||
private readonly id: number;
|
||||
|
||||
private email: string;
|
||||
|
||||
@@ -9,11 +9,11 @@ export default class User {
|
||||
|
||||
private accessToken: string;
|
||||
|
||||
constructor(email: string, password: string, accessToken: string) {
|
||||
constructor(email: string, password: string, accessToken?: string) {
|
||||
this.id = createIdUser();
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
this.accessToken = accessToken;
|
||||
this.password = password || '';
|
||||
this.accessToken = accessToken || '';
|
||||
}
|
||||
|
||||
get getPassword() {
|
||||
@@ -27,4 +27,12 @@ export default class User {
|
||||
get getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
get getEmail() {
|
||||
return this.email;
|
||||
}
|
||||
|
||||
get getId() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,13 @@ import { DataObject } from '../global/types';
|
||||
import FirebaseService from './firebaseService';
|
||||
|
||||
export default class CommonService<T> {
|
||||
public defaultPath: string = '/';
|
||||
private firebaseService = FirebaseService;
|
||||
public defaultPath: string;
|
||||
|
||||
public firebaseService = FirebaseService;
|
||||
|
||||
constructor(defaultPath?: string) {
|
||||
this.defaultPath = defaultPath ? defaultPath : '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to Firebase Database
|
||||
|
||||
@@ -13,6 +13,7 @@ import { DATABASE_URL } from '../constants/config';
|
||||
|
||||
class FirebaseService {
|
||||
private app: FirebaseApp;
|
||||
|
||||
private db: Database;
|
||||
|
||||
constructor() {
|
||||
@@ -119,7 +120,7 @@ class FirebaseService {
|
||||
onValue(
|
||||
ref(this.db, path),
|
||||
(snapshot) => {
|
||||
let listData: object[] = [];
|
||||
const listData: object[] = [];
|
||||
|
||||
// snapshot is a type of data by Firebase define
|
||||
snapshot.forEach((childSnapshot) => {
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
export default class Service {}
|
||||
import UserService from './userService';
|
||||
|
||||
export default class Service {
|
||||
public userService: UserService;
|
||||
|
||||
constructor() {
|
||||
this.userService = new UserService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@ export default class UserService {
|
||||
private _commonService: CommonService<User>;
|
||||
|
||||
constructor() {
|
||||
this._commonService = new CommonService<User>();
|
||||
this._commonService = new CommonService<User>('users/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save user into database
|
||||
* @param {Object} user The user object need to be saved into databae
|
||||
* @param {Object} user The user object need to be saved into database
|
||||
*/
|
||||
saveUser(user: User): void {
|
||||
this._commonService.save(user);
|
||||
async saveUser(user: User): Promise<void> {
|
||||
await this._commonService.save(user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
import CommonView from './commonView';
|
||||
import { ERROR_MESSAGE_DEFAULT } from '../constants/messages/dialog';
|
||||
import {
|
||||
INVALID_EMAIL_FORMAT,
|
||||
PASSWORD_NOT_MATCH,
|
||||
PASSWORD_NOT_STRONG,
|
||||
} from 'constants/messages/form';
|
||||
import {
|
||||
compare2Password,
|
||||
isValidPassword,
|
||||
isValidateEmail,
|
||||
renderRequiredText,
|
||||
} from '../helpers/validatorForm';
|
||||
|
||||
export default class AuthenticationView extends CommonView {
|
||||
public formEl: HTMLBodyElement | null;
|
||||
|
||||
public emailEl: HTMLBodyElement | null;
|
||||
|
||||
public messageDefault: string[];
|
||||
|
||||
public inputPasswordEl: HTMLBodyElement | null;
|
||||
|
||||
public inputPasswordConfirmEl: HTMLBodyElement | null;
|
||||
|
||||
public listError: string[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.formEl = document.querySelector('.form');
|
||||
this.emailEl = document.querySelector("[name='email']");
|
||||
this.messageDefault = ERROR_MESSAGE_DEFAULT;
|
||||
this.inputPasswordEl = document.querySelector('input[name="password"]');
|
||||
this.inputPasswordConfirmEl = document.querySelector(
|
||||
'input[name="password_confirm"]',
|
||||
);
|
||||
|
||||
this.listError = [];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
validateEmail(email: string): boolean {
|
||||
if (email) {
|
||||
if (!isValidateEmail(email)) {
|
||||
this.listError.push(INVALID_EMAIL_FORMAT);
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.emailEl) renderRequiredText('email', this.emailEl);
|
||||
return false;
|
||||
}
|
||||
|
||||
validatePassword(password: string): boolean {
|
||||
if (password) {
|
||||
if (!isValidPassword(password)) {
|
||||
this.listError.push(PASSWORD_NOT_STRONG);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (this.inputPasswordEl)
|
||||
renderRequiredText('password', this.inputPasswordEl);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
validatePasswordConfirm(password: string, passwordConfirm: string) {
|
||||
if (passwordConfirm) {
|
||||
if (!compare2Password(password, passwordConfirm)) {
|
||||
this.listError.push(PASSWORD_NOT_MATCH);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.inputPasswordConfirmEl)
|
||||
renderRequiredText('confirm password', this.inputPasswordConfirmEl);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear error message at form
|
||||
*/
|
||||
clearErrorMessage() {
|
||||
// Reassign again to check error message element haved on page or not
|
||||
const errorMessageEl = document.querySelector('.form__error-message');
|
||||
const errorTextEl = document.querySelectorAll('.error-text');
|
||||
|
||||
// If have error message on page, remove it with style error input password
|
||||
if (errorMessageEl || errorTextEl.length > 0) {
|
||||
if (errorMessageEl) errorMessageEl.remove();
|
||||
|
||||
if (errorTextEl) {
|
||||
errorTextEl.forEach((item) => {
|
||||
item.remove();
|
||||
});
|
||||
}
|
||||
|
||||
// Use for 2 form
|
||||
if (this.emailEl && this.inputPasswordEl) {
|
||||
this.emailEl.classList.remove('error-input');
|
||||
this.inputPasswordEl.classList.remove('error-input');
|
||||
}
|
||||
|
||||
// Only register form required
|
||||
if (this.inputPasswordConfirmEl)
|
||||
this.inputPasswordConfirmEl.classList.remove('error-input');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listener for input field at form
|
||||
*/
|
||||
addHandlerInputFormChange(): void {
|
||||
if (this.formEl)
|
||||
this.formEl.addEventListener('input', () => {
|
||||
this.clearErrorMessage();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error message with error style input password.
|
||||
* @param {string} message The error message you want show in form.
|
||||
*/
|
||||
showError(message: string[]): void {
|
||||
this.renderError(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error message in form
|
||||
* @param {string} message The message will show in form
|
||||
*/
|
||||
renderError(messages = this.messageDefault): void {
|
||||
if (messages.length > 0) {
|
||||
const messageItemMarkup = messages
|
||||
.map((message) => `<li>${message}</li>`)
|
||||
.join('\n');
|
||||
|
||||
const markup = `
|
||||
<ul class="form__error-message">
|
||||
${messageItemMarkup}
|
||||
</ul>
|
||||
`;
|
||||
|
||||
const titleEl = document.querySelector('.form__title');
|
||||
|
||||
if (titleEl) titleEl.insertAdjacentHTML('afterend', markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
import { MarkIcon, TypeToast } from '../constants/config';
|
||||
|
||||
export default class CommonView {
|
||||
public rootElement: HTMLBodyElement | null;
|
||||
|
||||
public toastDialog: HTMLDialogElement | null;
|
||||
|
||||
public toast: HTMLBodyElement | null;
|
||||
|
||||
public toastIcon: HTMLBodyElement | null;
|
||||
|
||||
public toastBtn: HTMLBodyElement | null;
|
||||
|
||||
public toastTitle: HTMLBodyElement | null;
|
||||
|
||||
public toastContent: HTMLBodyElement | null;
|
||||
|
||||
public spinner: HTMLBodyElement | null;
|
||||
|
||||
constructor() {
|
||||
this.rootElement = document.querySelector('body');
|
||||
this.toastDialog = document.querySelector('.dialog');
|
||||
this.toast = document.querySelector('.toast');
|
||||
|
||||
this.toastIcon = this.toast ? this.toast.querySelector('.mark') : null;
|
||||
this.toastBtn = this.toast
|
||||
? this.toast.querySelector('.toast__redirect-btn')
|
||||
: null;
|
||||
this.toastTitle = this.toast
|
||||
? this.toast.querySelector('.toast__title')
|
||||
: null;
|
||||
this.toastContent = this.toast
|
||||
? this.toast.querySelector('.toast__message')
|
||||
: null;
|
||||
|
||||
this.spinner = null;
|
||||
|
||||
// this.initToast();
|
||||
// this.initLoader();
|
||||
// this.handleEventToast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement toast in site
|
||||
*/
|
||||
initToast(): void {
|
||||
const markup = `
|
||||
<dialog class="dialog">
|
||||
<div class="toast">
|
||||
<div class="mark"></div>
|
||||
<h2 class="toast__title"></h2>
|
||||
<p class="toast__message"></p>
|
||||
<button class="toast__redirect-btn">OK</button>
|
||||
</div>
|
||||
</dialog>
|
||||
`;
|
||||
|
||||
if (this.rootElement)
|
||||
this.rootElement.insertAdjacentHTML('afterbegin', markup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide loader screen
|
||||
*/
|
||||
toggleLoaderSpinner(): void {
|
||||
if (this.spinner) this.spinner.classList.toggle('hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add toast content
|
||||
* @param {TYPE_TOAST} typeToast Type of the toast
|
||||
* @param {string} title Title of toast
|
||||
* @param {string} content Content of toast
|
||||
* @param {string} btnContent Content of button
|
||||
*/
|
||||
initToastContent(
|
||||
typeToast: string,
|
||||
title: string,
|
||||
content: string,
|
||||
btnContent: string,
|
||||
): void {
|
||||
// Remove old typeToast class if haved
|
||||
Object.keys(TypeToast).forEach((key) => {
|
||||
CommonView.removeClassElement(TypeToast[key as TypeToast], this.toast);
|
||||
});
|
||||
|
||||
// Remove old icon toast if haved
|
||||
Object.keys(MarkIcon).forEach((key) => {
|
||||
CommonView.removeClassElement(MarkIcon[key as MarkIcon], this.toastIcon);
|
||||
});
|
||||
|
||||
// Init content toast
|
||||
if (
|
||||
this.toast &&
|
||||
this.toastIcon &&
|
||||
this.toastTitle &&
|
||||
this.toastContent &&
|
||||
this.toastBtn
|
||||
) {
|
||||
this.toast.classList.add(
|
||||
typeToast === TypeToast.success ? TypeToast.success : TypeToast.error,
|
||||
);
|
||||
this.toastIcon.classList.add(
|
||||
typeToast === TypeToast.success ? MarkIcon.success : MarkIcon.error,
|
||||
);
|
||||
this.toastTitle.textContent = title;
|
||||
this.toastContent.textContent = content;
|
||||
this.toastBtn.textContent = btnContent;
|
||||
}
|
||||
}
|
||||
|
||||
static removeClassElement(classEl: string, el: HTMLBodyElement | null): void {
|
||||
if (el && el.classList.contains(classEl)) {
|
||||
el.classList.remove(classEl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listener for toast
|
||||
*/
|
||||
handleEventToast(): void {
|
||||
if (this.toastBtn) {
|
||||
this.toastBtn.addEventListener('click', () => {
|
||||
if (this.toastDialog) this.toastDialog.close();
|
||||
});
|
||||
}
|
||||
|
||||
// Add event close dialog when click outside
|
||||
if (this.toastDialog) {
|
||||
this.toastDialog.addEventListener('click', (e) => {
|
||||
if (this.toastDialog) {
|
||||
const dialogDimensions = this.toastDialog.getBoundingClientRect();
|
||||
if (
|
||||
e.clientX < dialogDimensions.left ||
|
||||
e.clientX > dialogDimensions.right ||
|
||||
e.clientY < dialogDimensions.top ||
|
||||
e.clientY > dialogDimensions.bottom
|
||||
) {
|
||||
this.toastDialog.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement loader screen
|
||||
*/
|
||||
initLoader() {
|
||||
const markup = `<div class="loader hidden"></div>`;
|
||||
|
||||
if (this.rootElement)
|
||||
this.rootElement.insertAdjacentHTML('afterbegin', markup);
|
||||
|
||||
// Init element
|
||||
this.spinner = document.querySelector('.loader');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import RegisterView from './registerView';
|
||||
|
||||
export default class View {
|
||||
public registerView: RegisterView;
|
||||
|
||||
constructor() {
|
||||
this.registerView = new RegisterView();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import { TypeToast, BTN_CONTENT } from '../constants/config';
|
||||
import AuthenticationView from './authenticationView';
|
||||
import User from '../models/user';
|
||||
import { redirectToLoginPage } from '../helpers/redirect';
|
||||
import {
|
||||
DEFAULT_MESSAGE,
|
||||
DEFAULT_TITLE_ERROR_TOAST,
|
||||
REGISTER_SUCCESS,
|
||||
USER_EXIST_ERROR,
|
||||
} from 'constants/messages/dialog';
|
||||
import { TError } from 'global/types';
|
||||
|
||||
export default class RegisterView extends AuthenticationView {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.toastBtn = document.querySelector('.toast__redirect-btn');
|
||||
}
|
||||
|
||||
async loadPage(getInfoUserLogin: () => Promise<User | null>) {
|
||||
this.toggleLoaderSpinner();
|
||||
|
||||
const user = await getInfoUserLogin();
|
||||
|
||||
if (user) {
|
||||
window.location.replace('/');
|
||||
}
|
||||
|
||||
this.toggleLoaderSpinner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data from user input
|
||||
* @returns {Object || null} Return object or null
|
||||
*/
|
||||
validateForm(): User | null {
|
||||
const form = document.getElementById('registerForm') as HTMLFormElement;
|
||||
const formData = new FormData(form);
|
||||
const email = formData.get('email') as string;
|
||||
const password = formData.get('password') as string;
|
||||
const passwordConfirm = formData.get('password_confirm') as string;
|
||||
|
||||
// Validate user input
|
||||
this.listError = []; // Reset list error
|
||||
const emailValid = this.validateEmail(email);
|
||||
const passwordValid = this.validatePassword(password);
|
||||
const passwordConfirmValid = this.validatePasswordConfirm(
|
||||
password,
|
||||
passwordConfirm,
|
||||
);
|
||||
|
||||
// Show error style
|
||||
if (this.emailEl && this.inputPasswordEl && this.inputPasswordConfirmEl) {
|
||||
this.emailEl.classList.toggle('error-input', !emailValid);
|
||||
this.inputPasswordEl.classList.toggle('error-input', !passwordValid);
|
||||
this.inputPasswordConfirmEl.classList.toggle(
|
||||
'error-input',
|
||||
!passwordConfirmValid,
|
||||
);
|
||||
}
|
||||
|
||||
if (emailValid && passwordValid && passwordConfirmValid) {
|
||||
return new User(email, password);
|
||||
}
|
||||
|
||||
this.showError(this.listError);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement register success toast in site
|
||||
*/
|
||||
showRegisterSuccessToast() {
|
||||
const typeToast = TypeToast.success;
|
||||
const title = REGISTER_SUCCESS;
|
||||
const content = DEFAULT_MESSAGE;
|
||||
const btnContent = BTN_CONTENT.OK;
|
||||
|
||||
this.initToastContent(typeToast, title, content, btnContent);
|
||||
|
||||
// Show toast
|
||||
if (this.toastDialog) this.toastDialog.showModal();
|
||||
|
||||
// Add event for toast button
|
||||
if (this.toastBtn)
|
||||
this.toastBtn.addEventListener('click', redirectToLoginPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement error toast in site
|
||||
* @param {string} content The content will show in error toast
|
||||
*/
|
||||
initErrorToast(error: TError | string): void {
|
||||
const title =
|
||||
typeof error === 'object' && error.title
|
||||
? error.title
|
||||
: DEFAULT_TITLE_ERROR_TOAST;
|
||||
|
||||
const content =
|
||||
typeof error === 'object' && error.message
|
||||
? error.message
|
||||
: (error as string);
|
||||
|
||||
this.initToastContent(TypeToast.error, title, content, BTN_CONTENT.OK);
|
||||
|
||||
if (this.toastDialog && this.toastBtn) {
|
||||
// Show toast
|
||||
this.toastDialog.showModal();
|
||||
|
||||
// Remove event for toast button
|
||||
this.toastBtn.removeEventListener('click', redirectToLoginPage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listener for form input
|
||||
* @param {Function} handler The function need to be set event
|
||||
*/
|
||||
addHandlerForm(
|
||||
checkExistUser: (email: string) => Promise<boolean>,
|
||||
saveUser: (user: User) => Promise<void>,
|
||||
) {
|
||||
if (this.formEl) {
|
||||
this.formEl.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.clearErrorMessage();
|
||||
this.submitForm(checkExistUser, saveUser);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async submitForm(
|
||||
checkExistUser: (email: string) => Promise<boolean>,
|
||||
saveUser: (user: User) => Promise<void>,
|
||||
) {
|
||||
try {
|
||||
// Load spinner
|
||||
this.toggleLoaderSpinner();
|
||||
|
||||
// Get validate form
|
||||
const user = this.validateForm();
|
||||
|
||||
// Save user
|
||||
if (user) {
|
||||
// Check user exist
|
||||
const userExist = await checkExistUser(user.getEmail);
|
||||
if (userExist) {
|
||||
throw Error(USER_EXIST_ERROR);
|
||||
} else {
|
||||
await saveUser(user);
|
||||
// Show toast success
|
||||
this.showRegisterSuccessToast();
|
||||
|
||||
// Clear form
|
||||
const formEl = document.getElementById(
|
||||
'registerForm',
|
||||
) as HTMLFormElement;
|
||||
|
||||
if (formEl) formEl.reset();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Show toast error
|
||||
this.initErrorToast(error as string | TError);
|
||||
}
|
||||
|
||||
// Close spinner
|
||||
this.toggleLoaderSpinner();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Language and Environment */
|
||||
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
"target": "ES2015" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
"lib": [
|
||||
"dom",
|
||||
"ES2017",
|
||||
|
||||
Reference in New Issue
Block a user