diff --git a/.gitignore b/.gitignore index 80704f4378..e66294da88 100755 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,4 @@ database.database database.db diagram.png __pycache__/ +migrations diff --git a/Pipfile b/Pipfile index 4d377014ae..f75fb3d98a 100644 --- a/Pipfile +++ b/Pipfile @@ -20,6 +20,7 @@ typing-extensions = "*" flask-jwt-extended = "==4.6.0" wtforms = "==3.1.2" sqlalchemy = "*" +flask-bcrypt = "*" [requires] python_version = "3.13" diff --git a/Pipfile.lock b/Pipfile.lock index d9e474e972..7f919a3beb 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ffbfb32d0afa5e4bcaba5c2d08c81381a97abd90f22284d2b76647365df5dc50" + "sha256": "9c612f7eebcf717533779dc02b40fce5eeaf66bfab71c81d166828128c272240" }, "pipfile-spec": 6, "requires": { @@ -24,6 +24,75 @@ "markers": "python_version >= '3.10'", "version": "==1.17.1" }, + "bcrypt": { + "hashes": [ + "sha256:046ad6db88edb3c5ece4369af997938fb1c19d6a699b9c1b27b0db432faae4c4", + "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a", + "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464", + "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", + "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746", + "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2", + "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41", + "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd", + "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9", + "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e", + "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538", + "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10", + "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb", + "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", + "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", + "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23", + "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", + "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75", + "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42", + "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a", + "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172", + "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683", + "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2", + "sha256:6b8f520b61e8781efee73cba14e3e8c9556ccfb375623f4f97429544734545b4", + "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba", + "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", + "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", + "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254", + "sha256:7edda91d5ab52b15636d9c30da87d2cc84f426c72b9dba7a9b4fe142ba11f534", + "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", + "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", + "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c", + "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83", + "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff", + "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", + "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", + "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", + "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", + "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", + "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac", + "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", + "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f", + "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", + "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86", + "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980", + "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd", + "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d", + "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1", + "sha256:dcd58e2b3a908b5ecc9b9df2f0085592506ac2d5110786018ee5e160f28e0911", + "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", + "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191", + "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4", + "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2", + "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8", + "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db", + "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927", + "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be", + "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb", + "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", + "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", + "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd", + "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822", + "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b" + ], + "markers": "python_version >= '3.8'", + "version": "==5.0.0" + }, "blinker": { "hashes": [ "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", @@ -42,11 +111,11 @@ }, "click": { "hashes": [ - "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", - "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4" + "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", + "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6" ], "markers": "python_version >= '3.10'", - "version": "==8.3.0" + "version": "==8.3.1" }, "cloudinary": { "hashes": [ @@ -58,12 +127,12 @@ }, "flask": { "hashes": [ - "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", - "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c" + "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", + "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==3.1.2" + "version": "==3.1.3" }, "flask-admin": { "hashes": [ @@ -74,6 +143,14 @@ "markers": "python_version >= '3.10'", "version": "==2.0.0" }, + "flask-bcrypt": { + "hashes": [ + "sha256:062fd991dc9118d05ac0583675507b9fe4670e44416c97e0e6819d03d01f808a", + "sha256:f07b66b811417ea64eb188ae6455b0b708a793d966e1a80ceec4a23bc42a4369" + ], + "index": "pypi", + "version": "==1.0.1" + }, "flask-cors": { "hashes": [ "sha256:c7b2cbfb1a31aa0d2e5341eea03a6805349f7a61647daee1a15c46bbe981494c", @@ -575,11 +652,11 @@ }, "werkzeug": { "hashes": [ - "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", - "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746" + "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25", + "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131" ], "markers": "python_version >= '3.9'", - "version": "==3.1.3" + "version": "==3.1.6" }, "wtforms": { "hashes": [ diff --git a/package-lock.json b/package-lock.json index 8d43d98ab7..56b4036ead 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.1", "license": "ISC", "dependencies": { + "phaser": "^3.90.0", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -877,19 +878,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", @@ -997,14 +985,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/node": { - "version": "16.11.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", - "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -1307,14 +1287,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -2212,6 +2184,12 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3385,6 +3363,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/phaser": { + "version": "3.90.0", + "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.90.0.tgz", + "integrity": "sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3915,29 +3902,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", @@ -4074,35 +4038,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/terser": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.38.1.tgz", - "integrity": "sha512-GWANVlPM/ZfYzuPHjq0nxT+EbOEDDN3Jwhwdg1D8TU8oSkktp8w64Uq4auuGLxFSoNTRDncTq2hQHX1Ld9KHkA==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4944,18 +4879,6 @@ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true }, - "@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, "@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", @@ -5044,14 +4967,6 @@ "@babel/types": "^7.20.7" } }, - "@types/node": { - "version": "16.11.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.12.tgz", - "integrity": "sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw==", - "dev": true, - "optional": true, - "peer": true - }, "@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -5252,14 +5167,6 @@ "update-browserslist-db": "^1.1.1" } }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "optional": true, - "peer": true - }, "call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -5880,6 +5787,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6647,6 +6559,14 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "phaser": { + "version": "3.90.0", + "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.90.0.tgz", + "integrity": "sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==", + "requires": { + "eventemitter3": "^5.0.1" + } + }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6982,28 +6902,6 @@ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "peer": true - } - } - }, "string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", @@ -7094,30 +6992,6 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, - "terser": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.38.1.tgz", - "integrity": "sha512-GWANVlPM/ZfYzuPHjq0nxT+EbOEDDN3Jwhwdg1D8TU8oSkktp8w64Uq4auuGLxFSoNTRDncTq2hQHX1Ld9KHkA==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true, - "peer": true - } - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/package.json b/package.json index 0caab10749..da70fb7cb6 100755 --- a/package.json +++ b/package.json @@ -8,10 +8,10 @@ "main": "index.js", "scripts": { "dev": "vite", - "start": "vite", - "build": "vite build", - "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" + "start": "vite", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" }, "author": { "name": "Alejandro Sanchez", @@ -30,13 +30,13 @@ "license": "ISC", "devDependencies": { "@types/react": "^18.2.18", - "@types/react-dom": "^18.2.7", - "@vitejs/plugin-react": "^4.0.4", - "eslint": "^8.46.0", - "eslint-plugin-react": "^7.33.1", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "vite": "^4.4.8" + "@types/react-dom": "^18.2.7", + "@vitejs/plugin-react": "^4.0.4", + "eslint": "^8.46.0", + "eslint-plugin-react": "^7.33.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "vite": "^4.4.8" }, "babel": { "presets": [ @@ -54,9 +54,10 @@ ] }, "dependencies": { + "phaser": "^3.90.0", "prop-types": "^15.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.18.0" + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.18.0" } } diff --git a/public/img/CasaDeCiroFn.png b/public/img/CasaDeCiroFn.png new file mode 100644 index 0000000000..3fffebaba2 Binary files /dev/null and b/public/img/CasaDeCiroFn.png differ diff --git a/public/img/TablaDerecha.png b/public/img/TablaDerecha.png new file mode 100644 index 0000000000..88a9cf86ed Binary files /dev/null and b/public/img/TablaDerecha.png differ diff --git a/public/img/TablaIzquierda.png b/public/img/TablaIzquierda.png new file mode 100644 index 0000000000..d83c17204a Binary files /dev/null and b/public/img/TablaIzquierda.png differ diff --git a/public/img/TablaMedio.png b/public/img/TablaMedio.png new file mode 100644 index 0000000000..86b972210f Binary files /dev/null and b/public/img/TablaMedio.png differ diff --git a/public/img/fondo.jpg b/public/img/fondo.jpg new file mode 100644 index 0000000000..7ac46ce050 Binary files /dev/null and b/public/img/fondo.jpg differ diff --git a/public/img/fondoLuz.jpg b/public/img/fondoLuz.jpg new file mode 100644 index 0000000000..bb177493c3 Binary files /dev/null and b/public/img/fondoLuz.jpg differ diff --git a/public/img/gatoNaranja.png b/public/img/gatoNaranja.png new file mode 100644 index 0000000000..a756cb1ef8 Binary files /dev/null and b/public/img/gatoNaranja.png differ diff --git a/public/img/gatoNaranjaFinal.png b/public/img/gatoNaranjaFinal.png new file mode 100644 index 0000000000..6fff92f9d9 Binary files /dev/null and b/public/img/gatoNaranjaFinal.png differ diff --git a/public/img/mundoRaro.png b/public/img/mundoRaro.png new file mode 100644 index 0000000000..f024255c05 Binary files /dev/null and b/public/img/mundoRaro.png differ diff --git a/public/img/perrito.png b/public/img/perrito.png new file mode 100644 index 0000000000..057a550eee Binary files /dev/null and b/public/img/perrito.png differ diff --git a/public/img/perritoDef.png b/public/img/perritoDef.png new file mode 100644 index 0000000000..4f18e0df86 Binary files /dev/null and b/public/img/perritoDef.png differ diff --git a/public/img/pezAzulSF.png b/public/img/pezAzulSF.png new file mode 100644 index 0000000000..cbcebb03a6 Binary files /dev/null and b/public/img/pezAzulSF.png differ diff --git a/public/img/puertaGato.png b/public/img/puertaGato.png new file mode 100644 index 0000000000..e7104a951b Binary files /dev/null and b/public/img/puertaGato.png differ diff --git a/public/img/sueloLargo.png b/public/img/sueloLargo.png new file mode 100644 index 0000000000..97f2577ffe Binary files /dev/null and b/public/img/sueloLargo.png differ diff --git a/src/api/models.py b/src/api/models.py index da515f6a1a..c9f706ac88 100644 --- a/src/api/models.py +++ b/src/api/models.py @@ -1,19 +1,112 @@ from flask_sqlalchemy import SQLAlchemy -from sqlalchemy import String, Boolean -from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String, Boolean, Integer, ForeignKey, DateTime +from sqlalchemy.orm import Mapped, mapped_column, relationship +from datetime import datetime +from flask_bcrypt import generate_password_hash db = SQLAlchemy() -class User(db.Model): - id: Mapped[int] = mapped_column(primary_key=True) - email: Mapped[str] = mapped_column(String(120), unique=True, nullable=False) - password: Mapped[str] = mapped_column(nullable=False) - is_active: Mapped[bool] = mapped_column(Boolean(), nullable=False) +class User (db.Model): + __tablename__ = "user" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + email: Mapped[str] = mapped_column(String(100), unique=True, nullable=False) + password_hash: Mapped[str] = mapped_column(String(100), nullable=False) + fecha_registro: Mapped[datetime] = mapped_column(DateTime, default=datetime.now) + + + def set_password(self, password): + self.password_hash = generate_password_hash(password).decode('utf-8') + + + michis: Mapped["Michi"] = relationship("Michi", back_populates="user") def serialize(self): return { "id": self.id, "email": self.email, - # do not serialize the password, its a security breach - } \ No newline at end of file + "fecha_registro": self.fecha_registro + } + + +class Michi (db.Model): + __tablename__ = "michi" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + user_id: Mapped[int] = mapped_column(ForeignKey("user.id"), nullable=False) + michi_name: Mapped[str] = mapped_column(String(50), nullable=False) + color: Mapped[str] = mapped_column(String(20)) + pescados_totales: Mapped[int] = mapped_column(Integer, default=0) + + user: Mapped["User"] = relationship("User", back_populates="michis") + partidas: Mapped[list["Partida"]] = relationship( + "Partida", back_populates="michis") + michi_inventario: Mapped[list["MichiInventario"]] = relationship( + "MichiInventario", back_populates="michi") + + def serialize(self): + return { + "id": self.id, + "user_id": self.user_id, + "michi_name": self.michi_name, + "color": self.color, + "pescados_totales": self.pescados_totales + } + + +class Accesorios(db.Model): + __tablename__ = "accesorios" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + accesorios_name: Mapped[str] = mapped_column(String(50), nullable=False) + tipo_de_accesorios: Mapped[str] = mapped_column(String(30)) + precio_pescado: Mapped[int] = mapped_column(Integer, nullable=False) + + michi_inventario: Mapped[list["MichiInventario"]] = relationship( + "MichiInventario", back_populates="accesorios") + + def serialize(self): + return { + "id": self.id, + "accesorios_name": self.accesorios_name, + "tipo_de_accesorios": self.tipo_de_accesorios, + "precio_pescado": self.precio_pescado + } + + +class MichiInventario (db.Model): + __tablename__ = "michi_inventario" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + michi_id: Mapped[int] = mapped_column( + ForeignKey("michi.id"), nullable=False) + accesorios_id: Mapped[int] = mapped_column( + ForeignKey("accesorios.id"), nullable=False) + esta_equipado: Mapped[bool] = mapped_column(Boolean, default=False) + + michi: Mapped["Michi"] = relationship( + "Michi", back_populates="michi_inventario") + accesorios: Mapped["Accesorios"] = relationship( + "Accesorios", back_populates="michi_inventario") + + def serialize(self): + return { + "id": self.id, + "michi_id": self.michi_id, + "accesorios_id": self.accesorios_id, + "esta_equipado": self.esta_equipado + } + + +class Partida(db.Model): + __tablename__ = "partida" + id: Mapped[int] = mapped_column(Integer, primary_key=True) + michi_id: Mapped[int] = mapped_column( + ForeignKey("michi.id"), nullable=False) + score: Mapped[int] = mapped_column(Integer, nullable=False) + + michis: Mapped["Michi"] = relationship("Michi", back_populates="partidas") + + def serialize(self): + return { + "id": self.id, + "michi_id": self.michi_id, + "score": self.score + } diff --git a/src/api/routes.py b/src/api/routes.py index 029589a3a1..ce5f89646c 100644 --- a/src/api/routes.py +++ b/src/api/routes.py @@ -2,7 +2,7 @@ This module takes care of starting the API Server, Loading the DB and Adding the endpoints """ from flask import Flask, request, jsonify, url_for, Blueprint -from api.models import db, User +from api.models import db, User, Michi from api.utils import generate_sitemap, APIException from flask_cors import CORS @@ -20,3 +20,34 @@ def handle_hello(): } return jsonify(response_body), 200 + + +@api.route('/signup', methods=['POST']) +def signup(): + data = request.get_json() + michi_name = data.get("michi_name") + email = data.get("email") + password = data.get("password") + confirm_password = data.get("confirm_password") + if not all([michi_name, email, password, confirm_password]): + return jsonify({"error": "Todo los campos son requeridos"}), 400 + if password != confirm_password: + return jsonify({"error": "Las contraseñas no coinciden"}), 400 + existing_user = db.session.execute(db.select(User).where(User.email == email)).scalar_one_or_none() + if existing_user: + return jsonify({"error": "Usuario con ese email ya existe"}), 400 + new_user = User(email=email) + new_user.set_password(password) + db.session.add(new_user) + db.session.flush() + new_michi = Michi( + michi_name = michi_name, + user_id=new_user.id, + color="Naranja", + pescados_totales=0 + ) + db.session.add(new_michi) + db.session.commit() + return jsonify ({"msg": "Usuario creado correctamente!"}), 201 + + diff --git a/src/front/Game/.gitkeep b/src/front/Game/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/front/Game/Animaciones/Animaciones.js b/src/front/Game/Animaciones/Animaciones.js new file mode 100644 index 0000000000..65db3d838a --- /dev/null +++ b/src/front/Game/Animaciones/Animaciones.js @@ -0,0 +1,23 @@ +export function Animaciones(Game){ + Game.anims.create({ + key: 'left', + frames: Game.anims.generateFrameNumbers('GatoNaranja', { start: 0, end: 2 }), + frameRate: 10, + repeat: -1, + }); + + Game.anims.create({ + key: 'turn', + frames: [{ key: 'GatoNaranja', frame: 3 }], + frameRate: 10, + repeat: -1, + }); + + Game.anims.create({ + key: 'leftPerro', + frames: Game.anims.generateFrameNumbers('Perrito', { start: 0, end: 1}), + frameRate: 10, + repeat: -1, + }); + +} \ No newline at end of file diff --git a/src/front/Game/Controles/Controles.js b/src/front/Game/Controles/Controles.js new file mode 100644 index 0000000000..c350f72cb7 --- /dev/null +++ b/src/front/Game/Controles/Controles.js @@ -0,0 +1,31 @@ + + +export function Controles(Game){ + + var cursors = Game.input.keyboard.createCursorKeys(); + + if (cursors.left.isDown) { + Game.GatoNar.setVelocityX(-160); + Game.GatoNar.setFlipX(false) + Game.GatoNar.anims.play('left', true) + } else if (cursors.right.isDown) { + Game.GatoNar.setVelocityX(160); + Game.GatoNar.anims.play('left', true) + Game.GatoNar.setFlipX(true) + } else { + Game.GatoNar.setVelocityX(0); + Game.GatoNar.anims.play('turn', true) + } + + if (cursors.up.isDown && Game.GatoNar.body.touching.down) { + Game.GatoNar.setVelocityY(-550); + + } + if (Game.Perrito.body.velocity.x < 0) { + Game.Perrito.setFlipX(false); + Game.Perrito.anims.play('leftPerro', true) + } else if (Game.Perrito.body.velocity.x > 0) { + Game.Perrito.setFlipX(true); + } + + } diff --git a/src/front/Game/scenes/Level2.js b/src/front/Game/scenes/Level2.js new file mode 100644 index 0000000000..ad4e70b2f9 --- /dev/null +++ b/src/front/Game/scenes/Level2.js @@ -0,0 +1,5 @@ +export default class Level2 extends Phaser.Scene { + constructor() { + super("Level2"); + } +} \ No newline at end of file diff --git a/src/front/Game/scenes/Level3.js b/src/front/Game/scenes/Level3.js new file mode 100644 index 0000000000..370359a1bf --- /dev/null +++ b/src/front/Game/scenes/Level3.js @@ -0,0 +1,5 @@ +export default class Level3 extends Phaser.Scene { + constructor() { + super("Level3"); + } +} \ No newline at end of file diff --git a/src/front/Game/scenes/MainScene.js b/src/front/Game/scenes/MainScene.js new file mode 100644 index 0000000000..c93fc149bb --- /dev/null +++ b/src/front/Game/scenes/MainScene.js @@ -0,0 +1,219 @@ +import Phaser from "phaser"; +import { Controles } from "../Controles/Controles"; +import { Animaciones } from "../Animaciones/Animaciones"; + + +export default class MainScene extends Phaser.Scene { + constructor() { + super("MainScene"); + } + + ScoreText = ""; + + preload() { + this.load.baseURL = "./"; + this.load.image("fondo", "img/fondo.jpg"); + this.load.image("fondoLargo", "img/mundoRaro.png"); + this.load.image("CasaCiro", "img/CasaDeCiroFn.png"); + this.load.image("TablaIzq", "img/TablaIzquierda.png"); + this.load.image("TablaDer", "img/TablaDerecha.png"); + this.load.image("TablaMedio", "img/TablaMedio.png"); + this.load.image("TablaLarga", "img/sueloLargo.png"); + this.load.image("Pez", "img/pezAzulSF.png"); + this.load.image("PuertaGato", "img/puertaGato.png"); + + this.load.spritesheet("GatoNaranja", "img/gatoNaranjaFinal.png", { + frameWidth: 48, + frameHeight: 31, + }); + this.load.spritesheet("Perrito", "img/perritoDef.png", { + frameWidth: 525, + frameHeight: 400, + }); + } + + create() { + this.GatoNar = ""; + this.Perrito = ""; + + // this.add.image(400, 330, 'fondo').setScale(0.8); + this.add.image(400, 760, "fondoLargo").setScale(1.1); + this.add.image(780, 470, "CasaCiro").setScale(0.6); + this.add.image(63, 75, "PuertaGato").setScale(0.4); + + var platforms = this.physics.add.staticGroup(); + + platforms.create(100, 180, "TablaLarga").setScale(0.8).refreshBody(); + platforms.create(400, 250, "TablaMedio").setScale(0.13).refreshBody(); + + platforms.create(700, 350, "TablaLarga").setScale(0.8).refreshBody(); + platforms.create(30, 500, "TablaLarga").setScale(0.8).refreshBody(); + platforms.create(760, 580, "TablaLarga").setScale(0.8).refreshBody(); + platforms.create(350, 400, "TablaMedio").setScale(0.13).refreshBody(); + platforms.create(400, 580, "TablaMedio").setScale(0.13).refreshBody(); + + platforms.create(265, 700, "TablaIzq").setScale(0.3).refreshBody(); + platforms.create(570, 800, "TablaDer").setScale(0.3).refreshBody(); + platforms.create(370, 870, "TablaMedio").setScale(0.13).refreshBody(); + platforms.create(570, 980, "TablaDer").setScale(0.3).refreshBody(); + + platforms.create(265, 1025, "TablaIzq").setScale(0.3).refreshBody(); + platforms.create(570, 1125, "TablaDer").setScale(0.3).refreshBody(); + platforms.create(265, 1250, "TablaIzq").setScale(0.3).refreshBody(); + platforms.create(570, 1350, "TablaDer").setScale(0.3).refreshBody(); + + platforms.children.iterate((plataforma) => { + plataforma.body.checkCollision.down = false; + plataforma.body.checkCollision.left = false; + plataforma.body.checkCollision.right = false; + }); + + var paredes = this.physics.add.staticGroup(); + + let ParedIzq = this.add.zone(180, 920, 20, 700); + this.physics.add.existing(ParedIzq, true); + paredes.add(ParedIzq); + + let ParedIzqHorizontal = this.add.zone(90, 560, 200, 20); + this.physics.add.existing(ParedIzqHorizontal, true); + paredes.add(ParedIzqHorizontal); + + let ParedDer = this.add.zone(660, 970, 20, 800); + this.physics.add.existing(ParedDer, true); + paredes.add(ParedDer); + + let ParedDerHorizontal = this.add.zone(750, 560, 200, 20); + this.physics.add.existing(ParedDerHorizontal, true); + paredes.add(ParedDerHorizontal); + + let sueloInicio = this.add.zone(400, 1450, 800, 20); + this.physics.add.existing(sueloInicio, true); + paredes.add(sueloInicio); + + let respawnDog = this.add.zone(15, 1400, 20, 20); + this.physics.add.existing(respawnDog, true); + + function Respawn(perrito, suelo) { + perrito.setPosition(770, 490); + this.Perrito.setVelocityX(150); + } + + function Morder() { + this.physics.pause(); + this.GatoNar.setTint(0xff0000); + + this.time.addEvent({ + delay: 2000, + loop: false, + callback: () => { + this.scene.start("endScene", { score: this.GatoNar.Score }); + }, + }); + } + + function Next () { + this.time.addEvent({ + delay: 1000, + loop: false, + callback: () => { + this.scene.start("endScene", { score: this.GatoNar.Score }); + }, + }); + } + let NextLevel = this.add.zone(75, 100, 20, 20); + this.physics.add.existing(NextLevel, true); + + this.Perrito = this.physics.add.sprite(770, 490, "Perrito").setScale(0.13); + this.Perrito.setCollideWorldBounds(true); + this.Perrito.setVelocityX(150); + this.Perrito.setBounce(1, 0); + this.physics.add.collider(this.Perrito, platforms); + this.physics.add.collider(this.Perrito, paredes); + this.physics.add.overlap(this.Perrito, respawnDog, Respawn, null, this); + + this.GatoNar = this.physics.add + .sprite(420, 1300, "GatoNaranja") + .setScale(1.6); + this.GatoNar.setCollideWorldBounds(true); + this.GatoNar.setBounce(0.1); + this.physics.add.collider( + this.GatoNar, + platforms, + null, + (gato, plataforma) => { + return ( + gato.body.velocity.y >= 0 && + gato.body.bottom <= plataforma.body.top + 20 + ); + }, + this, + ); + this.physics.add.collider(this.GatoNar, paredes); + this.physics.add.collider(this.Perrito, this.GatoNar, Morder, null, this); + this.physics.add.overlap(this.GatoNar, NextLevel, Next, null, this); + this.GatoNar.Score = 0; + + + + function PuntosGato(gato, pezTocando) { + pezTocando.disableBody(true, true); + this.GatoNar.Score += 25; + console.log("Puntos:", this.GatoNar.Score); + this.ScoreText.setText("Score: " + this.GatoNar.Score); + } + + var peces = this.physics.add.group(); + this.physics.add.collider(peces, platforms); + peces.create(170, 190, "Pez").setScale(0.07); + peces.create(570, 510, "Pez").setScale(0.07); + peces.create(265, 550, "Pez").setScale(0.07); + peces.create(570, 900, "Pez").setScale(0.07); + peces.create(265, 1150, "Pez").setScale(0.07); + + this.physics.add.overlap(this.GatoNar, peces, PuntosGato, null, this); + + Animaciones(this); + + this.physics.world.setBounds(0, 0, 800, 1500); + this.cameras.main.setBounds(0, 0, 800, 1500); + this.cameras.main.startFollow(this.GatoNar, true, 0.1, 0.1); + + this.gametime = 60; + this.timeTXT = this.add.text(350, 0, this.gametime, { + fontFamily: "font1", + fontSize: "64px", + fill: "#000", + }); + this.refreshTime(); + this.timeTXT.setScrollFactor(0); + + this.ScoreText = this.add.text(16, 16, "Score: 0", { + fontSize: "32px", + fill: "#000", + }); + this.ScoreText.setScrollFactor(0); + } + + refreshTime() { + this.gametime--; + this.timeTXT.setText(this.gametime); + if (this.gametime === 0) { + this.physics.pause(); + this.GatoNar.setTint(0xff0000); + + this.time.addEvent({ + delay: 1500, + loop: false, + callback: () => { + this.scene.start("endScene", { score: this.GatoNar.Score }); + }, + }); + } else { + this.time.delayedCall(1000, this.refreshTime, [], this); + } + } + + update() { + Controles(this); + } +} diff --git a/src/front/Game/scenes/endScene.js b/src/front/Game/scenes/endScene.js new file mode 100644 index 0000000000..d1cf0319e1 --- /dev/null +++ b/src/front/Game/scenes/endScene.js @@ -0,0 +1,61 @@ +import { Animaciones } from "../Animaciones/Animaciones"; + +export default class endScene extends Phaser.Scene { + constructor() { + super("endScene"); + } + + init(data) { + this.PuntosObtenidos = data.score || 0; + } + + preload() { + this.load.baseURL = "./"; + this.load.image("fondoLuz", "img/fondoLuz.jpg"); + + this.load.spritesheet("GatoNaranja", "img/gatoNaranjaFinal.png", { + frameWidth: 48, + frameHeight: 31, + }); + } + + create() { + this.add.image(400, 330, "fondoLuz").setScale(0.8); + this.add.text( + 100, + 150, + "Tu Gato ha hecho " + this.PuntosObtenidos + " puntos", + { fontSize: "32px", fill: "#fff" }, + ); + + var paredes = this.physics.add.staticGroup(); + + let sueloInicio = this.add.zone(400, 650, 800, 20); + this.physics.add.existing(sueloInicio, true); + paredes.add(sueloInicio); + + let GatoSentado = this.add.zone(80, 600, 20, 20); + this.physics.add.existing(GatoSentado, true); + + Animaciones(this); + + function Sentar(perrito, suelo) { + this.GatoNar.setVelocityX(0); + this.GatoNar.anims.play("turn", true); + } + + this.GatoNar = this.physics.add + .sprite(700, 600, "GatoNaranja") + .setScale(1.6); + this.GatoNar.setCollideWorldBounds(true); + this.physics.add.collider(this.GatoNar, paredes); + + // cinematica + this.GatoNar.setVelocityX(-160); + this.GatoNar.setFlipX(false); + this.GatoNar.anims.play("left", true); + this.physics.add.collider(GatoSentado, this.GatoNar, Sentar, null, this); + } + + update() {} +} diff --git a/src/front/Service/BackEndServices.js b/src/front/Service/BackEndServices.js new file mode 100644 index 0000000000..956de476cc --- /dev/null +++ b/src/front/Service/BackEndServices.js @@ -0,0 +1,18 @@ +export const signup = async (user) => { + const response = await fetch( + `${import.meta.env.VITE_BACKEND_URL}/api/signup`, + { + method: "POST", + body: JSON.stringify(user), + headers: { + "Content-type": "application/json", + }, + }, + ); + const data = await response.json(); + if (response.ok) { + return { success: true, data }; + } else { + return { success: false, error: data.error || "Error al registrarse" }; + } +}; diff --git a/src/front/components/Footer.jsx b/src/front/components/Footer.jsx deleted file mode 100644 index f06302dbd2..0000000000 --- a/src/front/components/Footer.jsx +++ /dev/null @@ -1,11 +0,0 @@ -export const Footer = () => ( - -); diff --git a/src/front/components/Footer/Footer.jsx b/src/front/components/Footer/Footer.jsx new file mode 100644 index 0000000000..a8e937311d --- /dev/null +++ b/src/front/components/Footer/Footer.jsx @@ -0,0 +1,21 @@ +import "./footer.css"; + +export const Footer = () => ( + +); diff --git a/src/front/components/Footer/footer.css b/src/front/components/Footer/footer.css new file mode 100644 index 0000000000..25dcaa647d --- /dev/null +++ b/src/front/components/Footer/footer.css @@ -0,0 +1,153 @@ +:root { + --sky-light: #8ECAE6; + --teal: #219EBC; + --deep-teal: #126782; + --cyan: #4adfe3; + --navy: #023047; + --dark: #0d1b2a; + --card: #0e2a45; + --gold: #FFB703; + --amber: #FD9E02; + --orange: #FB8500; + --gold-dim: #c07d10; + --border: #f5a623; + --text: #e8d9a0; + --pixel: 'Press Start 2P', monospace; +} + +.footer-callejon { + background: #0d1b26; + position: relative; + overflow: hidden; +} + +.callejon-pixeles { + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px), + repeating-linear-gradient( + 0deg, + transparent, + transparent 19px, + rgba(0, 0, 0, 0.2) 19px, + rgba(0, 0, 0, 0.2) 20px + ), + repeating-linear-gradient( + 90deg, + transparent, + transparent 29px, + rgba(0, 0, 0, 0.15) 29px, + rgba(0, 0, 0, 0.15) 30px + ); + background-size: + 30px 20px, + 30px 20px, + 100% 20px, + 30px 100%; +} + +.callejon-glow { + position: absolute; + top: -60px; + left: 50%; + transform: translateX(-50%); + width: 300px; + height: 200px; + background: radial-gradient( + ellipse, + rgba(255, 183, 3, 0.18) 0%, + transparent 70% + ); + pointer-events: none; +} + +.callejon-box { + position: absolute; + bottom: 0; + background: #6b4c2a; + border: 2px solid #4a3018; + image-rendering: pixelated; +} + +.callejon-box::after { + content: ""; + position: absolute; + top: 30%; + left: 10%; + right: 10%; + height: 2px; + background: rgba(0, 0, 0, 0.3); +} +.box-1 { + left: 20px; + width: 40px; + height: 36px; +} +.box-2 { + left: 48px; + width: 28px; + height: 48px; +} +.box-3 { + left: 18px; + bottom: 36px; + width: 34px; + height: 28px; +} +.box-4 { + right: 30px; + width: 44px; + height: 40px; +} +.box-5 { + right: 62px; + width: 30px; + height: 32px; +} +.box-6 { + right: 28px; + bottom: 40px; + width: 36px; + height: 30px; +} + +.callejon-footer { + position: relative; + z-index: 2; + max-width: 1100px; + margin: 0 auto; + padding: 40px 32px 28px; + display: grid; + grid-template-columns: 2fr 1fr 1fr; + gap: 40px; +} + +.callejon-marca h3 { + font-size: 16px; + font-family: var(--pixel); + color: var(--gold); + text-shadow: 2px 2px 0 var(--orange), 0 0 20px var(--gold); + margin-bottom: 12px; + animation: glow2 2.5s infinite alternate; +} + +@keyframes glow2 { + from { + text-shadow: 2px 2px 0 var(--orange), 0 0 8px var(--gold); + } + + to { + text-shadow: 2px 2px 0 var(--orange), 0 0 24px var(--gold), 0 0 40px var(--amber); + } +} + +.callejon-marca p { + font-size: 6px; + font-family: var(--pixel); + color: rgba(142, 202, 230, .5); + line-height: 2; + letter-spacing: 1px; +} + diff --git a/src/front/components/Navbar.jsx b/src/front/components/Navbar.jsx deleted file mode 100644 index 30d43a2636..0000000000 --- a/src/front/components/Navbar.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Link } from "react-router-dom"; - -export const Navbar = () => { - - return ( - - ); -}; \ No newline at end of file diff --git a/src/front/components/Navbar/Navbar.jsx b/src/front/components/Navbar/Navbar.jsx new file mode 100644 index 0000000000..2d10920ffb --- /dev/null +++ b/src/front/components/Navbar/Navbar.jsx @@ -0,0 +1,29 @@ +import { Link } from "react-router-dom"; +import "./navbar.css"; +import { useState } from "react"; +import { SignupModal } from "../SignupModal/SignupModal"; + + +export const Navbar = () => { + const [showSignup, setShowSignup] = useState(false); + return ( + <> + + > + ); +}; + + diff --git a/src/front/components/Navbar/navbar.css b/src/front/components/Navbar/navbar.css new file mode 100644 index 0000000000..fab3c0ffdb --- /dev/null +++ b/src/front/components/Navbar/navbar.css @@ -0,0 +1,111 @@ +:root { + --sky-light: #8ECAE6; + --teal: #219EBC; + --deep-teal: #126782; + --cyan: #4adfe3; + --navy: #023047; + --dark: #0d1b2a; + --card: #0e2a45; + --gold: #FFB703; + --amber: #FD9E02; + --orange: #FB8500; + --gold-dim: #c07d10; + --border: #f5a623; + --text: #e8d9a0; + --pixel: 'Press Start 2P', monospace; +} + +nav{ + position: relative; + z-index: 10; + width: 100%; + background: var(--navy); + border-bottom: 4px solid var(--gold); + background-image: repeating-linear-gradient(0deg, + transparent, + transparent 3px, + rgba(0, 0, 0, .12) 3px, + rgba(0, 0, 0, .12) 4px); + box-shadow: 0 4px 0 var(--orange), 0 8px 0 var(--amber); +} + +.nav-home { + max-width: 1200px; + margin: 0 auto; + padding: 0 24px; + height: 72px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +.logo { + display: flex; + align-items: center; + gap: 12px; + text-decoration: none; +} + +.logo-text { + font-family: var(--pixel); + font-size: 13px; + line-height: 1.5; + letter-spacing: 1px; +} + +.logo-text .principal { + color: var(--gold); + display: block; + font-size: 25px; + text-shadow: 2px 2px 0 var(--orange); + animation: glow 2.5s ease-in-out infinite alternate; +} + +.nav-button { + display: flex; + align-items: center; + gap: 12px; +} +.btn { + font-family: var(--pixel); + font-size: 9px; + padding: 10px 16px; + cursor: pointer; + border: none; + text-transform: uppercase; + letter-spacing: 1px; + position: relative; + transition: transform .1s, box-shadow .1s; + text-decoration: none; + display: inline-block; + line-height: 1; +} + +.btn:active { + transform: translate(2px, 2px); +} + +.btn-signin { + background: transparent; + color: var(--gold); + border: 2px solid var(--gold); + box-shadow: 3px 3px 0 var(--orange); +} + +.btn-signin:hover { + background: var(--gold); + color: var(--navy); + box-shadow: 3px 3px 0 var(--orange), inset 0 0 0 2px var(--amber); +} + +.btn-signup { + background: var(--gold); + color: var(--navy); + box-shadow: 3px 3px 0 var(--orange); +} + +.btn-signup:hover { + background: var(--amber); + box-shadow: 3px 3px 0 var(--orange), 0 0 12px var(--gold); +} \ No newline at end of file diff --git a/src/front/components/PhaserGame.jsx b/src/front/components/PhaserGame.jsx new file mode 100644 index 0000000000..9f3c6f966d --- /dev/null +++ b/src/front/components/PhaserGame.jsx @@ -0,0 +1,59 @@ +import { useEffect, useRef } from "react"; +import Phaser from "phaser"; + +import MainScene from "../Game/scenes/MainScene"; +import Level2 from "../Game/scenes/Level2"; +import Level3 from "../Game/scenes/Level3"; +import endScene from "../Game/scenes/endScene"; + + + +export default function PhaserGame() { + const gameRef = useRef(null); + const containerRef = useRef(null); + + useEffect(() => { + // Si ya hay una instancia, no crear otra (evita duplicados al hot-reload) + if (gameRef.current) return; + + // Configuración básica de Phaser. Aquí definimos el render, tamaño, + // sistema de físicas y las escenas que usaremos. + const config = { + // Phaser.AUTO: elige WebGL si está disponible, si no, usa Canvas + type: Phaser.AUTO, + // `parent` es el elemento DOM donde Phaser insertará el canvas + parent: containerRef.current, + width: 800, + height: 700, + backgroundColor: "#1d1d1d", + // escala automatica para las ditintas pantallas + scale: { + mode: Phaser.Scale.FIT}, + physics: { + // Usamos el sistema 'arcade' por ser sencillo para prototipos + default: "arcade", + arcade: { + gravity: { y: 1000}, + debug: false, + }, + }, + // Lista de escenas; aquí pasamos la clase que implementa la lógica + scene: [MainScene,Level2,Level3,endScene], + }; + + // Crear la instancia del juego con la configuración + gameRef.current = new Phaser.Game(config); + + // Cleanup: cuando el componente React se desmonta, destruye el juego + return () => { + gameRef.current?.destroy(true); + gameRef.current = null; + }; + }, []); + + return ( +
UNETE A LA BUSQUEDA
+ {status.msg && ( ++ ¿Ya tenés cuenta? Iniciar sesión +
+
-
-