diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index 952d7df..1e96e5a 100755 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -8,12 +8,15 @@ "name": "template", "version": "0.0.0", "dependencies": { + "@tailwindcss/vite": "^4.2.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-helmet": "^6.1.0", "react-lottie-player": "^1.5.5", "react-router-dom": "^6.19.0", - "sass": "^1.69.5" + "sass": "^1.69.5", + "tailwindcss": "^4.2.2", + "zustand": "^5.0.12" }, "devDependencies": { "@types/react": "^18.3.28", @@ -381,7 +384,6 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -393,7 +395,6 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -404,7 +405,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -418,7 +418,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "aix" @@ -434,7 +433,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -450,7 +448,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -466,7 +463,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "android" @@ -482,7 +478,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -498,7 +493,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -514,7 +508,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -530,7 +523,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "freebsd" @@ -546,7 +538,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -562,7 +553,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -578,7 +568,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "linux" @@ -594,7 +583,6 @@ "cpu": [ "loong64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -610,7 +598,6 @@ "cpu": [ "mips64el" ], - "dev": true, "optional": true, "os": [ "linux" @@ -626,7 +613,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -642,7 +628,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -658,7 +643,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -674,7 +658,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -708,7 +691,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "netbsd" @@ -742,7 +724,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "openbsd" @@ -776,7 +757,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "sunos" @@ -792,7 +772,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -808,7 +787,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -824,7 +802,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -938,33 +915,29 @@ "dev": true }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -973,14 +946,13 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -990,7 +962,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1631,7 +1602,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "android" @@ -1644,7 +1614,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "android" @@ -1657,7 +1626,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -1670,7 +1638,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "darwin" @@ -1683,7 +1650,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1696,7 +1662,6 @@ "cpu": [ "arm" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1709,7 +1674,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1722,7 +1686,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1735,7 +1698,6 @@ "cpu": [ "ppc64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1748,7 +1710,6 @@ "cpu": [ "riscv64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1761,7 +1722,6 @@ "cpu": [ "s390x" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1774,7 +1734,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1787,7 +1746,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "linux" @@ -1800,7 +1758,6 @@ "cpu": [ "arm64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -1813,7 +1770,6 @@ "cpu": [ "ia32" ], - "dev": true, "optional": true, "os": [ "win32" @@ -1826,7 +1782,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -1839,11 +1794,267 @@ "dev": true, "license": "MIT" }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", + "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "tailwindcss": "4.2.2" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1912,20 +2123,19 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "dev": true + "devOptional": true }, "node_modules/@types/react": { "version": "18.3.28", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -2424,7 +2634,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/debug": { @@ -2485,7 +2695,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -2509,6 +2718,19 @@ "integrity": "sha512-hohItzsQcG7/FBsviCYMtQwUSWvVF7NVqPOnJCErWsAshsP/CR2LAXdmq276RbESNdhxiAq5/vRo1g2pxGXVww==", "dev": true }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -2635,7 +2857,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -3120,7 +3341,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -3274,6 +3494,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3761,6 +3987,15 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3861,7 +4096,6 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -3894,7 +4128,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3915,7 +4148,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3936,7 +4168,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3957,7 +4188,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3978,7 +4208,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -3999,7 +4228,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4020,7 +4248,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4041,7 +4268,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4062,7 +4288,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4083,7 +4308,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4104,7 +4328,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4168,7 +4391,6 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -4196,7 +4418,6 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -4458,7 +4679,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -4478,7 +4698,6 @@ "version": "8.5.8", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4805,7 +5024,6 @@ "version": "4.17.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", - "dev": true, "dependencies": { "@types/estree": "1.0.5" }, @@ -5135,6 +5353,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5198,7 +5435,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true }, @@ -5363,7 +5599,6 @@ "version": "5.2.11", "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", - "dev": true, "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", @@ -6191,6 +6426,35 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", + "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/Frontend/package.json b/Frontend/package.json index a5fe1df..36ad1c2 100755 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -11,12 +11,15 @@ "test": "vitest run" }, "dependencies": { + "@tailwindcss/vite": "^4.2.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-helmet": "^6.1.0", "react-lottie-player": "^1.5.5", "react-router-dom": "^6.19.0", - "sass": "^1.69.5" + "sass": "^1.69.5", + "tailwindcss": "^4.2.2", + "zustand": "^5.0.12" }, "devDependencies": { "@types/react": "^18.3.28", diff --git a/Frontend/src/components/BoutiqueCard.jsx b/Frontend/src/components/BoutiqueCard.jsx index 2fe98d3..2548ad9 100755 --- a/Frontend/src/components/BoutiqueCard.jsx +++ b/Frontend/src/components/BoutiqueCard.jsx @@ -1,7 +1,9 @@ -import { useWildCoin } from "./WildCoin/WildCoinContext"; +// BoutiqueCard.jsx — Legacy shop card (shop.json boosters) +// TODO: Migrate to economy.ts generator system in a future step import "../scss/components/boutiquecard.scss"; import "../scss/components/buttons.scss"; import PropTypes from "prop-types"; +import { useGameStore } from "../store/useGameStore"; export default function BoutiqueCard({ name, @@ -9,89 +11,13 @@ export default function BoutiqueCard({ incrementValue, description, image, - link, type, - buyed, }) { - BoutiqueCard.propTypes = { - name: PropTypes.string.isRequired, - price: PropTypes.number.isRequired, - incrementValue: PropTypes.number.isRequired, - description: PropTypes.string.isRequired, - image: PropTypes.string.isRequired, - link: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - buyed: PropTypes.bool.isRequired, - }; + const resources = useGameStore((s) => s.state.resources); - const { - wildCoin, - incrementClick, - setWildCoin, - setIncrementClick, - incrementPerSecond, - setIncrementPerSecond, - setCoffee, - setSantaDrunk, - setManic, - setSnowman, - setBonnet, - setSugar, - setCookie, - setCouronne, - setEpice, - setBiere, - } = useWildCoin(); + // Legacy shop — disabled for now, generators are in GeneratorShop + const canAfford = resources >= price; - const acheterAmelioration = (type, price, name) => { - const prices = price; - const value = prices; - - - - if (wildCoin >= value) { - if (type === "actif") { - setIncrementClick(incrementClick + incrementValue); - } else if (type === "passif") { - setIncrementPerSecond(incrementPerSecond + incrementValue); - } - setWildCoin(wildCoin - value); - switch (name) { - case "Tasse à café": - setCoffee((prevCoffee) => [true, prevCoffee[1] + 1]); - break; - case "Manic": - setManic((prevManic) => [true, prevManic[1] + 1]); - break; - case "Bonnet": - setBonnet((prevBonnet) => [true, prevBonnet[1] + 1]); - break; - case "Mr Bonhomme": - setSnowman((prevSnowman) => [true, prevSnowman[1] + 1]); - break; - case "Canne en sucre": - setSugar((prevSugar) => [true, prevSugar[1] + 1]); - break; - case "Cookie": - setCookie((prevCookie) => [true, prevCookie[1] + 1]); - break; - case "Couronne d'hiver": - setCouronne((prevCouronne) => [true, prevCouronne[1] + 1]); - break; - case "Mr pain d'épice": - setEpice((prevEpice) => [true, prevEpice[1] + 1]); - break; - case "Bière": - setBiere((prevBiere) => [true, prevBiere[1] + 1]); - setSantaDrunk(true); - break; - default: - break; - } - } else { - console.log("Pas assez de WildCoin pour acheter cette amélioration."); - } - }; return (
@@ -105,7 +31,6 @@ export default function BoutiqueCard({

{name}

{price}

-
@@ -119,12 +44,22 @@ export default function BoutiqueCard({
); } + +BoutiqueCard.propTypes = { + name: PropTypes.string.isRequired, + price: PropTypes.number.isRequired, + incrementValue: PropTypes.number.isRequired, + description: PropTypes.string.isRequired, + image: PropTypes.string.isRequired, + type: PropTypes.string.isRequired, +}; diff --git a/Frontend/src/components/EvolutionTree.tsx b/Frontend/src/components/EvolutionTree.tsx new file mode 100644 index 0000000..1afec05 --- /dev/null +++ b/Frontend/src/components/EvolutionTree.tsx @@ -0,0 +1,99 @@ +// EvolutionTree.tsx — Arbre d'Évolution permanent (jamais reset) +// Visible après le premier prestige (prestigeCount >= 1) + +import React from "react"; +import { useGameStore } from "../store/useGameStore"; +import { canBuyEvolutionNode } from "../core/economy"; +import type { EvolutionNode } from "../core/economy"; + +const EFFECT_DESCRIPTIONS: Record string> = { + click_multiplier: (v) => `x${v} puissance de Ponte`, + production_multiplier: (v) => `x${v} production tous générateurs`, + start_bonus: (v) => `+${v} têtards au début de chaque run`, + unlock_generator: () => `Débloque le Lac Mystique dès le début`, + achievement_scaling: (v) => `+${(v * 100).toFixed(0)}% production par succès`, +}; + +function NodeCard({ + node, + canBuy, + onBuy, +}: { + node: EvolutionNode; + canBuy: boolean; + onBuy: () => void; +}) { + return ( +
+
+ {node.name} + + {node.unlocked ? "Débloqué" : `${node.cost} ADN`} + +
+

+ {EFFECT_DESCRIPTIONS[node.effect](node.value)} +

+ {!node.unlocked && ( + + )} +
+ ); +} + +export function EvolutionTree() { + const state = useGameStore((s) => s.state); + const buyNode = useGameStore((s) => s.buyNode); + const { evolutionTree, prestigeCount } = state; + + if (prestigeCount < 1) return null; + + return ( +
+
+

Arbre d'Évolution

+ {state.ancestralDna} ADN +
+
+ {evolutionTree.map((node, index) => ( + + {index > 0 && ( +
+ | +
+ )} + buyNode(node.id)} + /> +
+ ))} +
+
+ ); +} diff --git a/Frontend/src/components/GameTick.tsx b/Frontend/src/components/GameTick.tsx new file mode 100644 index 0000000..cd181a7 --- /dev/null +++ b/Frontend/src/components/GameTick.tsx @@ -0,0 +1,16 @@ +// GameTick.tsx — Lance le tick Zustand toutes les secondes +// À monter une seule fois dans l'arbre React (dans App) + +import { useEffect } from "react"; +import { useGameStore } from "../store/useGameStore"; + +export function GameTick() { + const tick = useGameStore((s) => s.tick); + + useEffect(() => { + const id = setInterval(tick, 1000); + return () => clearInterval(id); + }, [tick]); + + return null; +} diff --git a/Frontend/src/components/GeneratorShop.tsx b/Frontend/src/components/GeneratorShop.tsx new file mode 100644 index 0000000..a2afa83 --- /dev/null +++ b/Frontend/src/components/GeneratorShop.tsx @@ -0,0 +1,51 @@ +// GeneratorShop.tsx — Boutique de générateurs (economy.ts) +// Remplace Amelioration.jsx (legacy WildCoinContext) + +import { useGameStore } from "../store/useGameStore"; +import { formatNumber } from "../utils/formatNumber"; + +export function GeneratorShop() { + const generators = useGameStore((s) => s.state.generators); + const resources = useGameStore((s) => s.state.resources); + const buy = useGameStore((s) => s.buy); + const generatorCost = useGameStore((s) => s.generatorCost); + + return ( +
+

Générateurs

+ {generators.map((gen) => { + const cost = generatorCost(gen); + const canAfford = resources >= cost; + + return ( +
+
+ {gen.name} + + +{gen.baseProduction}/s · x{gen.owned} + +
+ +
+ ); + })} +
+ ); +} diff --git a/Frontend/src/components/Hud/Hud.jsx b/Frontend/src/components/Hud/Hud.jsx index b9363ec..6c73758 100755 --- a/Frontend/src/components/Hud/Hud.jsx +++ b/Frontend/src/components/Hud/Hud.jsx @@ -1,155 +1,41 @@ -import "../../scss/components/Hud.scss"; -import { useWildCoin } from "../WildCoin/WildCoinContext"; -import Timer from "../timer/Timer"; -import propTypes from "prop-types"; +// Hud.jsx — Stats HUD (Zustand) +import { useGameStore } from "../../store/useGameStore"; +import { formatNumber } from "../../utils/formatNumber"; + +const formatTime = (time) => { + const hours = Math.floor(time / 3600); + const minutes = Math.floor((time % 3600) / 60); + const secs = time % 60; + return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`; +}; function Hud({ isVisible }) { - Hud.propTypes = { - isVisible: propTypes.bool.isRequired, - }; + const resources = useGameStore((s) => s.state.resources); + const clickMultiplier = useGameStore((s) => s.state.clickMultiplier); + const productionPerSecond = useGameStore((s) => s.productionPerSecond); + const playSeconds = useGameStore((s) => s.playSeconds); - const { - manic, - snowman, - bonnet, - sugar, - cookie, - couronne, - epice, - biere, - coffee, - } = useWildCoin(); - - const { incrementClick, incrementPerSecond } = useWildCoin(); - const hiddenDiv = isVisible ? "none" : null; + if (isVisible) return null; return ( -
-
-
-

Temps de jeu

-

+
+
+
+ Temps + {formatTime(playSeconds)}
-
-

Auto CPS

-

{incrementPerSecond}

+
+ Têtards/s + {formatNumber(productionPerSecond)}
-
-

Player Click

-

{incrementClick}

+
+ Ponte + {clickMultiplier}
-
-
- {coffee[0] === true ? ( -
-
-
-

{coffee[1]}

-
-
- ) : null} - {manic[0] === true ? ( -
-
-
-

{manic[1]}

-
-
- ) : null} - {snowman[0] === true ? ( -
-
-
-

{snowman[1]}

-
-
- ) : null} - {bonnet[0] === true ? ( -
-
-
-

{bonnet[1]}

-
-
- ) : null} - {sugar[0] === true ? ( -
-
-
-

{sugar[1]}

-
-
- ) : null} - {cookie[0] === true ? ( -
-
-
-

{cookie[1]}

-
-
- ) : null} - {couronne[0] === true ? ( -
-
-
-

{couronne[1]}

-
-
- ) : null} - {epice[0] === true ? ( -
-
-
-

{epice[1]}

-
-
- ) : null} - {biere[0] === true ? ( -
-
-
-

{biere[1]}

-
-
- ) : null} -
+
+ {formatNumber(resources)} +
); } diff --git a/Frontend/src/components/MilestoneBar.tsx b/Frontend/src/components/MilestoneBar.tsx index 1386a2a..e16ec1b 100644 --- a/Frontend/src/components/MilestoneBar.tsx +++ b/Frontend/src/components/MilestoneBar.tsx @@ -1,50 +1,37 @@ // MilestoneBar.tsx — Progression vers le prochain prestige -// Barre visuelle ressources / 1 000 000 + indicateur restant +// Barre visuelle ressources / 1 000 000 -import React from "react"; +import { useGameStore } from "../store/useGameStore"; +import { formatNumber } from "../utils/formatNumber"; const PRESTIGE_THRESHOLD = 1_000_000; -interface MilestoneBarProps { - resources: number; -} +export function MilestoneBar() { + const resources = useGameStore((s) => s.state.resources); -export function MilestoneBar({ resources }: MilestoneBarProps) { const progress = Math.min(resources / PRESTIGE_THRESHOLD, 1); const progressPercent = (progress * 100).toFixed(1); const remaining = Math.max(PRESTIGE_THRESHOLD - resources, 0); - const formatNumber = (n: number): string => { - if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`; - if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`; - return Math.floor(n).toString(); - }; - return ( -
-
- Prochain prestige : {formatNumber(resources)} / {formatNumber(PRESTIGE_THRESHOLD)} +
+
+ Prochaine Génération + + {formatNumber(resources)} / {formatNumber(PRESTIGE_THRESHOLD)} +
-
+
- {remaining > 0 && ( -
- {formatNumber(remaining)} ressources restantes -
- )} - {remaining === 0 && ( -
Prestige disponible !
- )} +
+ {remaining > 0 + ? `${formatNumber(remaining)} têtards restants` + : "Nouvelle Génération disponible !"} +
); } diff --git a/Frontend/src/components/PrestigePanel.tsx b/Frontend/src/components/PrestigePanel.tsx index 13461df..40b36f0 100644 --- a/Frontend/src/components/PrestigePanel.tsx +++ b/Frontend/src/components/PrestigePanel.tsx @@ -1,54 +1,50 @@ -// PrestigePanel.tsx — Boucle de prestige long terme -// Visible uniquement quand canPrestige = true (ressources ≥ 1 000 000) +// PrestigePanel.tsx — Nouvelle Génération (prestige) +// Visible uniquement quand canPrestige = true (ressources >= 1 000 000) -import React from "react"; +import { useGameStore } from "../store/useGameStore"; +import { computePrestigeDna } from "../core/economy"; -interface PrestigePanelProps { - prestigeCount: number; - prestigeMultiplier: number; - canPrestige: boolean; - onPrestige: () => void; -} +export function PrestigePanel() { + const { prestigeCount, prestigeMultiplier, ancestralDna, lifetimeTadpoles } = + useGameStore((s) => s.state); + const canPrestige = useGameStore((s) => s.canPrestige); + const prestige = useGameStore((s) => s.prestige); + + const dnaPreview = computePrestigeDna(lifetimeTadpoles); -export function PrestigePanel({ - prestigeCount, - prestigeMultiplier, - canPrestige, - onPrestige, -}: PrestigePanelProps) { const handlePrestige = () => { const confirmed = window.confirm( - `Prestige — Reset total : ressources et générateurs à zéro.\n` + - `Récompense : +0.1× multiplicateur permanent.\n\n` + - `Multiplicateur actuel : ×${prestigeMultiplier.toFixed(1)}\n` + - `Multiplicateur après : ×${(prestigeMultiplier + 0.1).toFixed(1)}\n\n` + - `Confirmer le prestige ?` + `Nouvelle Génération\n\n` + + `Reset : têtards et générateurs à zéro.\n` + + `Récompense : +${dnaPreview} ADN Ancestral\n` + + ` +0.1x multiplicateur permanent\n\n` + + `ADN actuel : ${ancestralDna}\n` + + `ADN après : ${ancestralDna + dnaPreview}\n` + + `Multiplicateur : x${prestigeMultiplier.toFixed(1)} → x${(prestigeMultiplier + 0.1).toFixed(1)}\n\n` + + `L'Arbre d'Évolution persiste.\n\n` + + `Confirmer la Nouvelle Génération ?` ); - if (confirmed) { - onPrestige(); - } + if (confirmed) prestige(); }; return ( -
-
- Prestiges : {prestigeCount} - - Multiplicateur : ×{prestigeMultiplier.toFixed(1)} - +
+
+ Générations : {prestigeCount} + Mult : x{prestigeMultiplier.toFixed(1)} + ADN : {ancestralDna}
{canPrestige && ( -
-
- Récompense disponible : +0.1× multiplicateur permanent -
+
+

+ Nouvelle Génération : +{dnaPreview} ADN + +0.1x mult +

)} diff --git a/Frontend/src/components/WildCoin/Amelioration.jsx b/Frontend/src/components/WildCoin/Amelioration.jsx deleted file mode 100755 index d2e4404..0000000 --- a/Frontend/src/components/WildCoin/Amelioration.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useWildCoin } from "./WildCoinContext"; - -function Ameliorations() { - const { - wildCoin, - incrementClick, - setWildCoin, - setIncrementClick, - incrementPerSecond, - setIncrementPerSecond, - } = useWildCoin(); - - const activePrices = [5, 15, 50, 500]; // prix - const passivePrices = [5, 15, 50, 500]; - const activeIncrementValues = [1, 3, 10, 100]; // boost = incrementValue - const passiveIncrementValues = [1, 3, 10, 100]; // = incrementValue - - const acheterAmelioration = (type, amount) => { - const prices = type === "actif" ? activePrices : passivePrices; - const incrementValues = - type === "actif" ? activeIncrementValues : passiveIncrementValues; - - const price = prices[amount - 1]; - const incrementValue = incrementValues[amount - 1]; - - if (wildCoin >= price) { - if (type === "actif") { - setIncrementClick(incrementClick + incrementValue); - } else if (type === "passif") { - setIncrementPerSecond(incrementPerSecond + incrementValue); - } - setWildCoin(wildCoin - price); - } else { - console.log("Pas assez de WildCoin pour acheter cette amélioration."); - } - }; - - return ( -
-

Magasin d'Améliorations

-
-

Améliorations Actives :

- {[1, 2, 3, 4].map((amount) => ( -
- Price: {activePrices[amount - 1]} - (+ - {activeIncrementValues[amount - 1]}) - -
- ))} -
-
-

Améliorations Passives :

- {[1, 2, 3, 4].map((amount) => ( -
- Price: {passivePrices[amount - 1]} - (+ - {passiveIncrementValues[amount - 1]}) - -
- ))} -
-
- ); -} - -export default Ameliorations; diff --git a/Frontend/src/components/WildCoin/WildCoinContent.jsx b/Frontend/src/components/WildCoin/WildCoinContent.jsx deleted file mode 100755 index 0d5c920..0000000 --- a/Frontend/src/components/WildCoin/WildCoinContent.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import { createContext, useContext, useState, useEffect } from "react"; - -export const WildCoinContext = createContext(); - -export const useWildCoin = () => { - return useContext(WildCoinContext); -}; - -export function WildCoinProvider({ children }) { - // Value of coin - const [wildCoin, setWildCoin] = useState(0); - // increment by click state - const [incrementClick, setIncrementClick] = useState(1); - // increment inner useEffect state - const [incrementPerSecond, setIncrementPerSecond] = useState(1); - - const incrementWildCoin = (amount) => { - setWildCoin((prevWildCoin) => prevWildCoin + amount); - }; - /** - * @passiveGenerationInterval incre per sec wild coin in wildCoin - * */ - useEffect(() => { - const passiveGenerationInterval = setInterval(() => { - incrementWildCoin(incrementPerSecond); - }, 1000); - - return () => clearInterval(passiveGenerationInterval); - }, [incrementPerSecond]); - - const value = { - wildCoin, - setWildCoin, - incrementClick, - incrementWildCoin, - }; - - return ( - - {children} - - ); -} diff --git a/Frontend/src/components/WildCoin/WildCoinContext.jsx b/Frontend/src/components/WildCoin/WildCoinContext.jsx deleted file mode 100755 index 009bd8d..0000000 --- a/Frontend/src/components/WildCoin/WildCoinContext.jsx +++ /dev/null @@ -1,138 +0,0 @@ -import { createContext, useContext, useState, useEffect } from "react"; - -export const WildCoinContext = createContext(); -export const useWildCoin = () => { - return useContext(WildCoinContext); -}; - -export function WildCoinProvider({ children }) { - const initialState = { - wildCoin: 0, - incrementClick: 1, - incrementPerSecond: 0, - }; - - const [state, setState] = useState(() => { - const storedContext = JSON.parse(localStorage.getItem("wildCoinContext")); - return { - ...initialState, - ...(storedContext || {}), - }; - }); - - const [coffee, setCoffee] = useState([false, 0]); - const [manic, setManic] = useState([false, 0]); - const [snowman, setSnowman] = useState([false, 0]); - const [bonnet, setBonnet] = useState([false, 0]); - const [sugar, setSugar] = useState([false, 0]); - const [cookie, setCookie] = useState([false, 0]); - const [couronne, setCouronne] = useState([false, 0]); - const [epice, setEpice] = useState([false, 0]); - const [biere, setBiere] = useState([false, 0]); - - const [santaDrunk, setSantaDrunk] = useState(false); - - const updateWildCoin = (amount) => { - setState((prev) => ({ - ...prev, - wildCoin: prev.wildCoin + amount, - })); - }; - - const incrementWildCoin = (amount) => { - updateWildCoin(amount); - }; - - const setIncrementClick = (amount) => { - setState((prev) => ({ - ...prev, - incrementClick: amount, - })); - }; - - const setIncrementPerSecond = (amount) => { - setState((prev) => ({ - ...prev, - incrementPerSecond: amount, - })); - }; - - const setWildCoin = (amount) => { - setState((prev) => ({ - ...prev, - wildCoin: amount, - })); - }; - - const [seconds, setSeconds] = useState(0); - - useEffect(() => { - const intervalId = setInterval(() => { - setSeconds((prevSeconds) => prevSeconds + 1); - }, 1000); - - return () => clearInterval(intervalId); - }, []); - - const formatTime = (time) => { - const hours = Math.floor(time / 3600); - const minutes = Math.floor((time % 3600) / 60); - const seconds = time % 60; - - const formattedTime = `${hours < 10 ? '0' : ''}${hours}:${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`; - - return formattedTime; - }; - - - - useEffect(() => { - localStorage.setItem("wildCoinContext", JSON.stringify(state)); - }, [state]); - - - useEffect(() => { - const passiveGenerationInterval = setInterval(() => { - updateWildCoin(state.incrementPerSecond); - }, 1000); - - return () => clearInterval(passiveGenerationInterval); - }, [state.incrementPerSecond]); - - const contextValue = { - ...state, - incrementWildCoin, - setIncrementClick, - setIncrementPerSecond, - setWildCoin, - coffee, - setCoffee, - manic, - setManic, - snowman, - setSnowman, - bonnet, - setBonnet, - sugar, - setSugar, - cookie, - setCookie, - couronne, - setCouronne, - epice, - setEpice, - biere, - setBiere, - setSantaDrunk, - santaDrunk, - seconds, - setSeconds, - formatTime, - }; - - return ( - - {children} - - ); -} diff --git a/Frontend/src/components/WildCoin/WildCoinIncrementAction.jsx b/Frontend/src/components/WildCoin/WildCoinIncrementAction.jsx deleted file mode 100755 index b5a8206..0000000 --- a/Frontend/src/components/WildCoin/WildCoinIncrementAction.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import { useWildCoin } from "./WildCoinContext"; -import WildCoinS from "../../../public/WildCoin.svg"; - -function WildCoinIncrementAction() { - const { incrementClick, incrementWildCoin } = useWildCoin(); - - const handleIncrement = () => { - incrementWildCoin(incrementClick); - }; - - return ( - Clique pour augmenter le score - ); -} - -export default WildCoinIncrementAction; diff --git a/Frontend/src/components/timer/Timer.jsx b/Frontend/src/components/timer/Timer.jsx deleted file mode 100755 index ca0c0d5..0000000 --- a/Frontend/src/components/timer/Timer.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import { useWildCoin } from "../WildCoin/WildCoinContext"; - -function Timer() { - const { formatTime, seconds } = useWildCoin(); - - return ( -
-

{formatTime(seconds)}

-
- ); -} - -export default Timer; diff --git a/Frontend/src/hooks/useEconomy.ts b/Frontend/src/hooks/useEconomy.ts deleted file mode 100644 index 7e23465..0000000 --- a/Frontend/src/hooks/useEconomy.ts +++ /dev/null @@ -1,93 +0,0 @@ -// useEconomy.ts — Hook React avec lazy calculation + localStorage -// Pas de setInterval pour les gains passifs — tout est calculé au read - -import { useState, useCallback, useEffect } from "react"; -import { - GameState, - DEFAULT_STATE, - applyIdleGains, - applyClick, - buyGenerator, - applyPrestige, - canPrestige, - totalProductionPerSecond, - generatorCost, -} from "../core/economy"; - -const SAVE_KEY = "clickerz_state"; - -function loadState(): GameState { - try { - const raw = localStorage.getItem(SAVE_KEY); - if (!raw) return { ...DEFAULT_STATE, lastTick: Date.now() }; - const saved = JSON.parse(raw) as GameState; - // Appliquer les gains idle accumulés pendant l'absence - return applyIdleGains(saved, Date.now()); - } catch { - return { ...DEFAULT_STATE, lastTick: Date.now() }; - } -} - -function saveState(state: GameState): void { - localStorage.setItem(SAVE_KEY, JSON.stringify(state)); -} - -export function useEconomy() { - const [state, setState] = useState(loadState); - - // Auto-save + tick UI toutes les secondes (pour rafraîchir l'affichage uniquement) - // La vraie valeur est calculée lazily dans totalProductionPerSecond - useEffect(() => { - const id = setInterval(() => { - setState((prev) => { - const updated = applyIdleGains(prev, Date.now()); - saveState(updated); - return updated; - }); - }, 1000); - return () => clearInterval(id); - }, []); - - const click = useCallback(() => { - setState((prev) => { - const updated = applyClick(applyIdleGains(prev, Date.now())); - saveState(updated); - return updated; - }); - }, []); - - const buy = useCallback((genId: string) => { - setState((prev) => { - const withIdle = applyIdleGains(prev, Date.now()); - const updated = buyGenerator(withIdle, genId); - if (!updated) return prev; - saveState(updated); - return updated; - }); - }, []); - - const prestige = useCallback(() => { - setState((prev) => { - if (!canPrestige(prev)) return prev; - const updated = applyPrestige(prev); - saveState(updated); - return updated; - }); - }, []); - - const reset = useCallback(() => { - const fresh = { ...DEFAULT_STATE, lastTick: Date.now() }; - saveState(fresh); - setState(fresh); - }, []); - - return { - state, - click, - buy, - prestige, - canPrestige: canPrestige(state), - productionPerSecond: totalProductionPerSecond(state), - generatorCost, - }; -} diff --git a/Frontend/src/index.css b/Frontend/src/index.css index 1156e1c..2c653ff 100755 --- a/Frontend/src/index.css +++ b/Frontend/src/index.css @@ -1,7 +1,9 @@ +@import "tailwindcss"; + :root { margin: 0; padding: 0; - + } ::-webkit-scrollbar { width: 1px; diff --git a/Frontend/src/pages/Achievements.jsx b/Frontend/src/pages/Achievements.jsx index b119c19..78e414a 100755 --- a/Frontend/src/pages/Achievements.jsx +++ b/Frontend/src/pages/Achievements.jsx @@ -1,14 +1,14 @@ import { useState } from "react"; import AchievementsCard from "../components/AchievementsCard"; import "../scss/achievements.scss"; -import { useWildCoin } from "../components/WildCoin/WildCoinContext"; +import { useGameStore } from "../store/useGameStore"; import achievements from "../data/Achievements.json"; function Achievements() { - const { wildCoin } = useWildCoin(); + const resources = useGameStore((s) => s.state.resources); let score = 1; - if (wildCoin >= 25) { - score = Math.floor((wildCoin - 25) / 400) + 1; + if (resources >= 25) { + score = Math.floor((resources - 25) / 400) + 1; } else { score = 0; } diff --git a/Frontend/src/scss/components/Hud.scss b/Frontend/src/scss/components/Hud.scss deleted file mode 100755 index a4b620f..0000000 --- a/Frontend/src/scss/components/Hud.scss +++ /dev/null @@ -1,102 +0,0 @@ -.hudContainer { - display: flex; - flex-direction: column; - flex-wrap: wrap; - align-items: center; - justify-content: space-around; - - min-width: 260px; - width: fit-content; - max-width: 1280px; - height: fit-content; - - gap: 1rem; - padding: 1rem; - border-radius: 8px; - box-sizing: border-box; - - background-color: var(--color-grey); - color: var(--color-white); - - font-family: var(--font); - color: aliceblue; - font-size: 1rem; - text-align: center; - - position: absolute; - left: 50%; - transform: translate(-50%); - z-index: 2; - - .hudStats { - display: flex; - flex-wrap: wrap; - gap: 0.8rem; - - .section { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; - } - } - - .hudBooster { - display: flex; - flex-wrap: wrap; - justify-content: space-around; - - align-items: center; - padding: 1rem; - gap: 1rem; - - min-width: 280px; - width: auto; - max-width: 1280px; - height: fit-content; - - color: var(--color-white); - font-family: var(--font); - color: aliceblue; - font-size: 1rem; - text-align: center; - - border-radius: 8px; - box-sizing: border-box; - - .boosterItem { - display: flex; - flex-wrap: wrap; - width: 30px; - height: 30px; - gap: 0.6rem; - - .boosterIcon { - width: 100%; - background-size: contain; - background-repeat: no-repeat; - background-position: center; - } - .countbox { - position: absolute; - margin-top: 20px; - margin-left: 15px; - border: solid 1px var(--color-white); - background-color: var(--color-white); - border-radius: 20%; - padding: 0.1rem; - color: var(--color-grey); - min-width: 20px; - width: fit-content; - height: 20px; - box-shadow: -1px -1px 7px 0px var(--color-grey); - } - .boosterCount { - font-family: var(--font); - font-size: 0.7rem; - text-align: center; - font-weight: 900; - } - } - } -} diff --git a/Frontend/src/store/useGameStore.ts b/Frontend/src/store/useGameStore.ts new file mode 100644 index 0000000..08c1b94 --- /dev/null +++ b/Frontend/src/store/useGameStore.ts @@ -0,0 +1,146 @@ +// useGameStore.ts — Zustand store, source unique de l'état game +// Lazy calculation pattern : gains passifs calculés au read depuis lastTick + +import { create } from "zustand"; +import { + GameState, + DEFAULT_STATE, + applyIdleGains, + applyClick, + buyGenerator, + buyEvolutionNode, + applyPrestige, + canPrestige as canPrestigeCheck, + totalProductionPerSecond, + generatorCost as genCost, +} from "../core/economy"; + +const SAVE_KEY = "clickerz_state"; + +function loadState(): GameState { + try { + const raw = localStorage.getItem(SAVE_KEY); + if (!raw) return { ...DEFAULT_STATE, lastTick: Date.now() }; + const saved = JSON.parse(raw) as GameState; + return applyIdleGains(saved, Date.now()); + } catch { + return { ...DEFAULT_STATE, lastTick: Date.now() }; + } +} + +function saveState(state: GameState): void { + localStorage.setItem(SAVE_KEY, JSON.stringify(state)); +} + +interface GameStore { + // State + state: GameState; + playSeconds: number; + + // Derived (recalculated on tick) + canPrestige: boolean; + productionPerSecond: number; + + // Actions + tick: () => void; + click: () => void; + buy: (genId: string) => void; + buyNode: (nodeId: string) => void; + prestige: () => void; + reset: () => void; + loadFromServer: (serverState: GameState) => void; + generatorCost: typeof genCost; +} + +export const useGameStore = create((set, get) => ({ + state: loadState(), + playSeconds: 0, + canPrestige: canPrestigeCheck(loadState()), + productionPerSecond: totalProductionPerSecond(loadState()), + + tick: () => { + set((s) => { + const updated = applyIdleGains(s.state, Date.now()); + saveState(updated); + return { + state: updated, + playSeconds: s.playSeconds + 1, + canPrestige: canPrestigeCheck(updated), + productionPerSecond: totalProductionPerSecond(updated), + }; + }); + }, + + click: () => { + set((s) => { + const updated = applyClick(applyIdleGains(s.state, Date.now())); + saveState(updated); + return { + state: updated, + canPrestige: canPrestigeCheck(updated), + productionPerSecond: totalProductionPerSecond(updated), + }; + }); + }, + + buy: (genId: string) => { + set((s) => { + const withIdle = applyIdleGains(s.state, Date.now()); + const updated = buyGenerator(withIdle, genId); + if (!updated) return s; + saveState(updated); + return { + state: updated, + productionPerSecond: totalProductionPerSecond(updated), + }; + }); + }, + + buyNode: (nodeId: string) => { + set((s) => { + const updated = buyEvolutionNode(s.state, nodeId); + if (!updated) return s; + saveState(updated); + return { + state: updated, + productionPerSecond: totalProductionPerSecond(updated), + }; + }); + }, + + prestige: () => { + set((s) => { + if (!canPrestigeCheck(s.state)) return s; + const updated = applyPrestige(s.state); + saveState(updated); + return { + state: updated, + canPrestige: canPrestigeCheck(updated), + productionPerSecond: totalProductionPerSecond(updated), + }; + }); + }, + + reset: () => { + const fresh = { ...DEFAULT_STATE, lastTick: Date.now() }; + saveState(fresh); + set({ + state: fresh, + playSeconds: 0, + canPrestige: false, + productionPerSecond: 0, + }); + }, + + loadFromServer: (serverState: GameState) => { + const hydrated = applyIdleGains(serverState, Date.now()); + saveState(hydrated); + set({ + state: hydrated, + canPrestige: canPrestigeCheck(hydrated), + productionPerSecond: totalProductionPerSecond(hydrated), + }); + }, + + generatorCost: genCost, +})); diff --git a/Frontend/vite.config.js b/Frontend/vite.config.js index a083a0b..1d7b0de 100755 --- a/Frontend/vite.config.js +++ b/Frontend/vite.config.js @@ -1,8 +1,9 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' export default defineConfig({ - plugins: [react()], + plugins: [tailwindcss(), react()], test: { environment: 'node', },