feat(sprint1-step5): migration Tailwind v4 + Zustand — suppression WildCoinContext
- Install tailwindcss @tailwindcss/vite zustand - useGameStore.ts : Zustand store wrappant economy.ts (tick, click, buy, prestige, buyNode, loadFromServer) - GameTick.tsx : composant timer 1s - GeneratorShop.tsx : boutique générateurs Tailwind (remplace Amelioration.jsx) - EvolutionTree, PrestigePanel, MilestoneBar : rewrite Zustand + Tailwind - Hud.jsx : rewrite Zustand + Tailwind (suppression Hud.scss) - BoutiqueCard, Achievements : migrés vers Zustand - Supprimé : WildCoin/ (4 fichiers), timer/Timer.jsx, useEconomy.ts, Hud.scss - WildCoinProvider retiré de main.jsx
This commit is contained in:
458
Frontend/package-lock.json
generated
458
Frontend/package-lock.json
generated
@@ -8,12 +8,15 @@
|
|||||||
"name": "template",
|
"name": "template",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tailwindcss/vite": "^4.2.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-lottie-player": "^1.5.5",
|
"react-lottie-player": "^1.5.5",
|
||||||
"react-router-dom": "^6.19.0",
|
"react-router-dom": "^6.19.0",
|
||||||
"sass": "^1.69.5"
|
"sass": "^1.69.5",
|
||||||
|
"tailwindcss": "^4.2.2",
|
||||||
|
"zustand": "^5.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.3.28",
|
"@types/react": "^18.3.28",
|
||||||
@@ -381,7 +384,6 @@
|
|||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz",
|
||||||
"integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==",
|
"integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -393,7 +395,6 @@
|
|||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz",
|
||||||
"integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==",
|
"integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -404,7 +405,6 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
|
||||||
"integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
|
"integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -418,7 +418,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"aix"
|
"aix"
|
||||||
@@ -434,7 +433,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
@@ -450,7 +448,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
@@ -466,7 +463,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
@@ -482,7 +478,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
@@ -498,7 +493,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
@@ -514,7 +508,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
@@ -530,7 +523,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"freebsd"
|
"freebsd"
|
||||||
@@ -546,7 +538,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -562,7 +553,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -578,7 +568,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -594,7 +583,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -610,7 +598,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -626,7 +613,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -642,7 +628,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -658,7 +643,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -674,7 +658,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -708,7 +691,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"netbsd"
|
"netbsd"
|
||||||
@@ -742,7 +724,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"openbsd"
|
"openbsd"
|
||||||
@@ -776,7 +757,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"sunos"
|
"sunos"
|
||||||
@@ -792,7 +772,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -808,7 +787,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -824,7 +802,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -938,33 +915,29 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||||
"dev": true,
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/set-array": "^1.0.1",
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
"@jridgewell/trace-mapping": "^0.3.24"
|
||||||
"@jridgewell/trace-mapping": "^0.3.9"
|
}
|
||||||
},
|
},
|
||||||
"engines": {
|
"node_modules/@jridgewell/remapping": {
|
||||||
"node": ">=6.0.0"
|
"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": {
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||||
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
|
"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": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
@@ -973,14 +946,13 @@
|
|||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.20",
|
"version": "0.3.31",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||||
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
|
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||||
"dev": true,
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.1.0",
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
@@ -990,7 +962,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
||||||
"integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
|
"integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1631,7 +1602,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
@@ -1644,7 +1614,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
@@ -1657,7 +1626,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
@@ -1670,7 +1638,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
@@ -1683,7 +1650,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -1696,7 +1662,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -1709,7 +1674,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -1722,7 +1686,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -1735,7 +1698,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -1748,7 +1710,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -1761,7 +1722,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -1774,7 +1734,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -1787,7 +1746,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
@@ -1800,7 +1758,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -1813,7 +1770,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -1826,7 +1782,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
@@ -1839,11 +1794,267 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@tybys/wasm-util": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||||
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
|
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1912,20 +2123,19 @@
|
|||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/prop-types": {
|
"node_modules/@types/prop-types": {
|
||||||
"version": "15.7.11",
|
"version": "15.7.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
||||||
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
|
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.3.28",
|
"version": "18.3.28",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz",
|
||||||
"integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==",
|
"integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
@@ -2424,7 +2634,7 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
@@ -2485,7 +2695,6 @@
|
|||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -2509,6 +2718,19 @@
|
|||||||
"integrity": "sha512-hohItzsQcG7/FBsviCYMtQwUSWvVF7NVqPOnJCErWsAshsP/CR2LAXdmq276RbESNdhxiAq5/vRo1g2pxGXVww==",
|
"integrity": "sha512-hohItzsQcG7/FBsviCYMtQwUSWvVF7NVqPOnJCErWsAshsP/CR2LAXdmq276RbESNdhxiAq5/vRo1g2pxGXVww==",
|
||||||
"dev": true
|
"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": {
|
"node_modules/es-abstract": {
|
||||||
"version": "1.22.3",
|
"version": "1.22.3",
|
||||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
|
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz",
|
||||||
@@ -2635,7 +2857,6 @@
|
|||||||
"version": "0.20.2",
|
"version": "0.20.2",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
|
||||||
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
|
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"esbuild": "bin/esbuild"
|
"esbuild": "bin/esbuild"
|
||||||
@@ -3120,7 +3341,6 @@
|
|||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3274,6 +3494,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/graphemer": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||||
@@ -3761,6 +3987,15 @@
|
|||||||
"set-function-name": "^2.0.1"
|
"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": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@@ -3861,7 +4096,6 @@
|
|||||||
"version": "1.32.0",
|
"version": "1.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
||||||
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
|
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"detect-libc": "^2.0.3"
|
"detect-libc": "^2.0.3"
|
||||||
@@ -3894,7 +4128,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3915,7 +4148,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3936,7 +4168,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3957,7 +4188,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3978,7 +4208,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -3999,7 +4228,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -4020,7 +4248,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -4041,7 +4268,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -4062,7 +4288,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -4083,7 +4308,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -4104,7 +4328,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -4168,7 +4391,6 @@
|
|||||||
"version": "0.30.21",
|
"version": "0.30.21",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
@@ -4196,7 +4418,6 @@
|
|||||||
"version": "3.3.11",
|
"version": "3.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -4458,7 +4679,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
@@ -4478,7 +4698,6 @@
|
|||||||
"version": "8.5.8",
|
"version": "8.5.8",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
|
||||||
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
|
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -4805,7 +5024,6 @@
|
|||||||
"version": "4.17.2",
|
"version": "4.17.2",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
|
||||||
"integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
|
"integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.5"
|
"@types/estree": "1.0.5"
|
||||||
},
|
},
|
||||||
@@ -5135,6 +5353,25 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/text-table": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||||
@@ -5198,7 +5435,6 @@
|
|||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"dev": true,
|
|
||||||
"license": "0BSD",
|
"license": "0BSD",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
@@ -5363,7 +5599,6 @@
|
|||||||
"version": "5.2.11",
|
"version": "5.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz",
|
||||||
"integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==",
|
"integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.20.1",
|
"esbuild": "^0.20.1",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
@@ -6191,6 +6426,35 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,15 @@
|
|||||||
"test": "vitest run"
|
"test": "vitest run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tailwindcss/vite": "^4.2.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-lottie-player": "^1.5.5",
|
"react-lottie-player": "^1.5.5",
|
||||||
"react-router-dom": "^6.19.0",
|
"react-router-dom": "^6.19.0",
|
||||||
"sass": "^1.69.5"
|
"sass": "^1.69.5",
|
||||||
|
"tailwindcss": "^4.2.2",
|
||||||
|
"zustand": "^5.0.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.3.28",
|
"@types/react": "^18.3.28",
|
||||||
|
|||||||
@@ -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/boutiquecard.scss";
|
||||||
import "../scss/components/buttons.scss";
|
import "../scss/components/buttons.scss";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
import { useGameStore } from "../store/useGameStore";
|
||||||
|
|
||||||
export default function BoutiqueCard({
|
export default function BoutiqueCard({
|
||||||
name,
|
name,
|
||||||
@@ -9,89 +11,13 @@ export default function BoutiqueCard({
|
|||||||
incrementValue,
|
incrementValue,
|
||||||
description,
|
description,
|
||||||
image,
|
image,
|
||||||
link,
|
|
||||||
type,
|
type,
|
||||||
buyed,
|
|
||||||
}) {
|
}) {
|
||||||
BoutiqueCard.propTypes = {
|
const resources = useGameStore((s) => s.state.resources);
|
||||||
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 {
|
// Legacy shop — disabled for now, generators are in GeneratorShop
|
||||||
wildCoin,
|
const canAfford = resources >= price;
|
||||||
incrementClick,
|
|
||||||
setWildCoin,
|
|
||||||
setIncrementClick,
|
|
||||||
incrementPerSecond,
|
|
||||||
setIncrementPerSecond,
|
|
||||||
setCoffee,
|
|
||||||
setSantaDrunk,
|
|
||||||
setManic,
|
|
||||||
setSnowman,
|
|
||||||
setBonnet,
|
|
||||||
setSugar,
|
|
||||||
setCookie,
|
|
||||||
setCouronne,
|
|
||||||
setEpice,
|
|
||||||
setBiere,
|
|
||||||
} = useWildCoin();
|
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<div className="shopcardcontainer">
|
<div className="shopcardcontainer">
|
||||||
<div className="shopcontainer">
|
<div className="shopcontainer">
|
||||||
@@ -105,7 +31,6 @@ export default function BoutiqueCard({
|
|||||||
<p className="itemname">{name}</p>
|
<p className="itemname">{name}</p>
|
||||||
<div className="price">
|
<div className="price">
|
||||||
<p className="itemprice">{price}</p>
|
<p className="itemprice">{price}</p>
|
||||||
|
|
||||||
<div className="priceicon" />
|
<div className="priceicon" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -119,12 +44,22 @@ export default function BoutiqueCard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => acheterAmelioration(type, price, name)}
|
disabled={!canAfford}
|
||||||
className="primary-button"
|
className="primary-button"
|
||||||
|
style={{ opacity: canAfford ? 1 : 0.5 }}
|
||||||
>
|
>
|
||||||
Acheter
|
Bientôt
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|||||||
99
Frontend/src/components/EvolutionTree.tsx
Normal file
99
Frontend/src/components/EvolutionTree.tsx
Normal file
@@ -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, (value: number) => 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 (
|
||||||
|
<div
|
||||||
|
className={`flex flex-col gap-2 p-3 rounded-lg border text-sm transition-colors ${
|
||||||
|
node.unlocked
|
||||||
|
? "border-emerald-500/50 bg-emerald-950/30"
|
||||||
|
: canBuy
|
||||||
|
? "border-amber-500/50 bg-amber-950/20"
|
||||||
|
: "border-gray-700/50 bg-gray-800/30 opacity-50"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-white font-semibold">{node.name}</span>
|
||||||
|
<span className="text-xs text-gray-400">
|
||||||
|
{node.unlocked ? "Débloqué" : `${node.cost} ADN`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-300">
|
||||||
|
{EFFECT_DESCRIPTIONS[node.effect](node.value)}
|
||||||
|
</p>
|
||||||
|
{!node.unlocked && (
|
||||||
|
<button
|
||||||
|
disabled={!canBuy}
|
||||||
|
onClick={onBuy}
|
||||||
|
className={`px-3 py-1 rounded text-xs font-medium transition-colors cursor-pointer ${
|
||||||
|
canBuy
|
||||||
|
? "bg-amber-600 hover:bg-amber-500 text-white"
|
||||||
|
: "bg-gray-700 text-gray-500 cursor-not-allowed"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{canBuy ? "Débloquer" : "Verrouillé"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<div className="flex flex-col gap-3 p-4 rounded-xl bg-gray-900/80 backdrop-blur-sm max-w-md w-full">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<h3 className="text-lg font-bold text-white">Arbre d'Évolution</h3>
|
||||||
|
<span className="text-sm text-amber-300">{state.ancestralDna} ADN</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{evolutionTree.map((node, index) => (
|
||||||
|
<React.Fragment key={node.id}>
|
||||||
|
{index > 0 && (
|
||||||
|
<div
|
||||||
|
className={`text-center text-xs ${
|
||||||
|
evolutionTree[index - 1].unlocked
|
||||||
|
? "text-emerald-400"
|
||||||
|
: "text-gray-600"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
|
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<NodeCard
|
||||||
|
node={node}
|
||||||
|
canBuy={canBuyEvolutionNode(state, node.id)}
|
||||||
|
onBuy={() => buyNode(node.id)}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
16
Frontend/src/components/GameTick.tsx
Normal file
16
Frontend/src/components/GameTick.tsx
Normal file
@@ -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;
|
||||||
|
}
|
||||||
51
Frontend/src/components/GeneratorShop.tsx
Normal file
51
Frontend/src/components/GeneratorShop.tsx
Normal file
@@ -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 (
|
||||||
|
<div className="flex flex-col gap-3 p-4 rounded-xl bg-gray-900/80 backdrop-blur-sm max-w-md w-full">
|
||||||
|
<h2 className="text-lg font-bold text-white">Générateurs</h2>
|
||||||
|
{generators.map((gen) => {
|
||||||
|
const cost = generatorCost(gen);
|
||||||
|
const canAfford = resources >= cost;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={gen.id}
|
||||||
|
className={`flex items-center justify-between gap-3 p-3 rounded-lg border transition-colors ${
|
||||||
|
canAfford
|
||||||
|
? "border-emerald-500/50 bg-emerald-950/30 hover:bg-emerald-950/50"
|
||||||
|
: "border-gray-700/50 bg-gray-800/30 opacity-60"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col min-w-0">
|
||||||
|
<span className="text-white font-semibold text-sm">{gen.name}</span>
|
||||||
|
<span className="text-gray-400 text-xs">
|
||||||
|
+{gen.baseProduction}/s · x{gen.owned}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => buy(gen.id)}
|
||||||
|
disabled={!canAfford}
|
||||||
|
className={`shrink-0 px-3 py-1.5 rounded-md text-sm font-medium transition-colors cursor-pointer ${
|
||||||
|
canAfford
|
||||||
|
? "bg-emerald-600 hover:bg-emerald-500 text-white"
|
||||||
|
: "bg-gray-700 text-gray-500 cursor-not-allowed"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{formatNumber(cost)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,155 +1,41 @@
|
|||||||
import "../../scss/components/Hud.scss";
|
// Hud.jsx — Stats HUD (Zustand)
|
||||||
import { useWildCoin } from "../WildCoin/WildCoinContext";
|
import { useGameStore } from "../../store/useGameStore";
|
||||||
import Timer from "../timer/Timer";
|
import { formatNumber } from "../../utils/formatNumber";
|
||||||
import propTypes from "prop-types";
|
|
||||||
|
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 }) {
|
function Hud({ isVisible }) {
|
||||||
Hud.propTypes = {
|
const resources = useGameStore((s) => s.state.resources);
|
||||||
isVisible: propTypes.bool.isRequired,
|
const clickMultiplier = useGameStore((s) => s.state.clickMultiplier);
|
||||||
};
|
const productionPerSecond = useGameStore((s) => s.productionPerSecond);
|
||||||
|
const playSeconds = useGameStore((s) => s.playSeconds);
|
||||||
|
|
||||||
const {
|
if (isVisible) return null;
|
||||||
manic,
|
|
||||||
snowman,
|
|
||||||
bonnet,
|
|
||||||
sugar,
|
|
||||||
cookie,
|
|
||||||
couronne,
|
|
||||||
epice,
|
|
||||||
biere,
|
|
||||||
coffee,
|
|
||||||
} = useWildCoin();
|
|
||||||
|
|
||||||
const { incrementClick, incrementPerSecond } = useWildCoin();
|
|
||||||
const hiddenDiv = isVisible ? "none" : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="hudContainer">
|
<div className="fixed top-20 left-1/2 -translate-x-1/2 z-10 flex flex-col items-center gap-2 px-6 py-3 rounded-xl bg-gray-900/90 backdrop-blur-sm text-white font-[var(--font)]">
|
||||||
<div style={{ display: hiddenDiv }} className="hudStats">
|
<div className="flex gap-6 text-sm">
|
||||||
<div className="time section">
|
<div className="flex flex-col items-center">
|
||||||
<p>Temps de jeu</p>
|
<span className="text-gray-400 text-xs">Temps</span>
|
||||||
<p><Timer /></p>
|
<span>{formatTime(playSeconds)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="auto section">
|
<div className="flex flex-col items-center">
|
||||||
<p>Auto CPS</p>
|
<span className="text-gray-400 text-xs">Têtards/s</span>
|
||||||
<p>{incrementPerSecond}</p>
|
<span>{formatNumber(productionPerSecond)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="player section">
|
<div className="flex flex-col items-center">
|
||||||
<p>Player Click</p>
|
<span className="text-gray-400 text-xs">Ponte</span>
|
||||||
<p>{incrementClick}</p>
|
<span>{clickMultiplier}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="hudBooster">
|
<div className="text-lg font-bold text-emerald-400">
|
||||||
{coffee[0] === true ? (
|
{formatNumber(resources)}
|
||||||
<div className="boosterItem">
|
</div>
|
||||||
<div
|
|
||||||
className="boosterIcon"
|
|
||||||
style={{ backgroundImage: `url(/svg/Tasse.svg)` }}
|
|
||||||
alt="coffee"
|
|
||||||
/>
|
|
||||||
<div className="countbox">
|
|
||||||
<p className="boosterCount">{coffee[1]}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{manic[0] === true ? (
|
|
||||||
<div className="boosterItem">
|
|
||||||
<div
|
|
||||||
className="boosterIcon"
|
|
||||||
style={{ backgroundImage: `url(/svg/Hand.svg)` }}
|
|
||||||
alt="coffee"
|
|
||||||
/>
|
|
||||||
<div className="countbox">
|
|
||||||
<p className="boosterCount">{manic[1]}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{snowman[0] === true ? (
|
|
||||||
<div className="boosterItem">
|
|
||||||
<div
|
|
||||||
className="boosterIcon"
|
|
||||||
style={{ backgroundImage: `url(/svg/Bonhome.svg)` }}
|
|
||||||
alt="coffee"
|
|
||||||
/>
|
|
||||||
<div className="countbox">
|
|
||||||
<p className="boosterCount">{snowman[1]}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{bonnet[0] === true ? (
|
|
||||||
<div className="boosterItem">
|
|
||||||
<div
|
|
||||||
className="boosterIcon"
|
|
||||||
style={{ backgroundImage: `url(/svg/Bonnet.svg)` }}
|
|
||||||
alt="coffee"
|
|
||||||
/>
|
|
||||||
<div className="countbox">
|
|
||||||
<p className="boosterCount">{bonnet[1]}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{sugar[0] === true ? (
|
|
||||||
<div className="boosterItem">
|
|
||||||
<div
|
|
||||||
className="boosterIcon"
|
|
||||||
style={{ backgroundImage: `url(/svg/Canne.svg)` }}
|
|
||||||
alt="coffee"
|
|
||||||
/>
|
|
||||||
<div className="countbox">
|
|
||||||
<p className="boosterCount">{sugar[1]}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{cookie[0] === true ? (
|
|
||||||
<div className="boosterItem">
|
|
||||||
<div
|
|
||||||
className="boosterIcon"
|
|
||||||
style={{ backgroundImage: `url(/svg/Cookie.svg)` }}
|
|
||||||
alt="coffee"
|
|
||||||
/>
|
|
||||||
<div className="countbox">
|
|
||||||
<p className="boosterCount">{cookie[1]}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{couronne[0] === true ? (
|
|
||||||
<div className="boosterItem">
|
|
||||||
<div
|
|
||||||
className="boosterIcon"
|
|
||||||
style={{ backgroundImage: `url(/svg/Courone.svg)` }}
|
|
||||||
alt="coffee"
|
|
||||||
/>
|
|
||||||
<div className="countbox">
|
|
||||||
<p className="boosterCount">{couronne[1]}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{epice[0] === true ? (
|
|
||||||
<div className="boosterItem">
|
|
||||||
<div
|
|
||||||
className="boosterIcon"
|
|
||||||
style={{ backgroundImage: `url(/svg/PainDep.svg)` }}
|
|
||||||
alt="coffee"
|
|
||||||
/>
|
|
||||||
<div className="countbox">
|
|
||||||
<p className="boosterCount">{epice[1]}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{biere[0] === true ? (
|
|
||||||
<div className="boosterItem">
|
|
||||||
<div
|
|
||||||
className="boosterIcon"
|
|
||||||
style={{ backgroundImage: `url(/svg/Beer.svg)` }}
|
|
||||||
alt="coffee"
|
|
||||||
/>
|
|
||||||
<div className="countbox">
|
|
||||||
<p className="boosterCount">{biere[1]}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,37 @@
|
|||||||
// MilestoneBar.tsx — Progression vers le prochain prestige
|
// 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;
|
const PRESTIGE_THRESHOLD = 1_000_000;
|
||||||
|
|
||||||
interface MilestoneBarProps {
|
export function MilestoneBar() {
|
||||||
resources: number;
|
const resources = useGameStore((s) => s.state.resources);
|
||||||
}
|
|
||||||
|
|
||||||
export function MilestoneBar({ resources }: MilestoneBarProps) {
|
|
||||||
const progress = Math.min(resources / PRESTIGE_THRESHOLD, 1);
|
const progress = Math.min(resources / PRESTIGE_THRESHOLD, 1);
|
||||||
const progressPercent = (progress * 100).toFixed(1);
|
const progressPercent = (progress * 100).toFixed(1);
|
||||||
const remaining = Math.max(PRESTIGE_THRESHOLD - resources, 0);
|
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 (
|
return (
|
||||||
<div className="milestone-bar" aria-label="Progression vers le prestige">
|
<div className="flex flex-col gap-1 max-w-md w-full">
|
||||||
<div className="milestone-label">
|
<div className="text-xs text-gray-300 flex justify-between">
|
||||||
Prochain prestige : {formatNumber(resources)} / {formatNumber(PRESTIGE_THRESHOLD)}
|
<span>Prochaine Génération</span>
|
||||||
|
<span>
|
||||||
|
{formatNumber(resources)} / {formatNumber(PRESTIGE_THRESHOLD)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="h-2 bg-gray-800 rounded-full overflow-hidden">
|
||||||
className="milestone-track"
|
|
||||||
role="progressbar"
|
|
||||||
aria-valuenow={Math.floor(progress * 100)}
|
|
||||||
aria-valuemin={0}
|
|
||||||
aria-valuemax={100}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className="milestone-fill"
|
className="h-full bg-gradient-to-r from-purple-600 to-purple-400 transition-all duration-500 rounded-full"
|
||||||
style={{ width: `${progressPercent}%` }}
|
style={{ width: `${progressPercent}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{remaining > 0 && (
|
<div className="text-xs text-gray-400 text-right">
|
||||||
<div className="milestone-remaining">
|
{remaining > 0
|
||||||
{formatNumber(remaining)} ressources restantes
|
? `${formatNumber(remaining)} têtards restants`
|
||||||
</div>
|
: "Nouvelle Génération disponible !"}
|
||||||
)}
|
</div>
|
||||||
{remaining === 0 && (
|
|
||||||
<div className="milestone-ready">Prestige disponible !</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,50 @@
|
|||||||
// PrestigePanel.tsx — Boucle de prestige long terme
|
// PrestigePanel.tsx — Nouvelle Génération (prestige)
|
||||||
// Visible uniquement quand canPrestige = true (ressources ≥ 1 000 000)
|
// 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 {
|
export function PrestigePanel() {
|
||||||
prestigeCount: number;
|
const { prestigeCount, prestigeMultiplier, ancestralDna, lifetimeTadpoles } =
|
||||||
prestigeMultiplier: number;
|
useGameStore((s) => s.state);
|
||||||
canPrestige: boolean;
|
const canPrestige = useGameStore((s) => s.canPrestige);
|
||||||
onPrestige: () => void;
|
const prestige = useGameStore((s) => s.prestige);
|
||||||
}
|
|
||||||
|
const dnaPreview = computePrestigeDna(lifetimeTadpoles);
|
||||||
|
|
||||||
export function PrestigePanel({
|
|
||||||
prestigeCount,
|
|
||||||
prestigeMultiplier,
|
|
||||||
canPrestige,
|
|
||||||
onPrestige,
|
|
||||||
}: PrestigePanelProps) {
|
|
||||||
const handlePrestige = () => {
|
const handlePrestige = () => {
|
||||||
const confirmed = window.confirm(
|
const confirmed = window.confirm(
|
||||||
`Prestige — Reset total : ressources et générateurs à zéro.\n` +
|
`Nouvelle Génération\n\n` +
|
||||||
`Récompense : +0.1× multiplicateur permanent.\n\n` +
|
`Reset : têtards et générateurs à zéro.\n` +
|
||||||
`Multiplicateur actuel : ×${prestigeMultiplier.toFixed(1)}\n` +
|
`Récompense : +${dnaPreview} ADN Ancestral\n` +
|
||||||
`Multiplicateur après : ×${(prestigeMultiplier + 0.1).toFixed(1)}\n\n` +
|
` +0.1x multiplicateur permanent\n\n` +
|
||||||
`Confirmer le prestige ?`
|
`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) {
|
if (confirmed) prestige();
|
||||||
onPrestige();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="prestige-panel">
|
<div className="flex flex-col gap-2 p-4 rounded-xl bg-purple-900/60 backdrop-blur-sm max-w-md w-full">
|
||||||
<div className="prestige-stats">
|
<div className="flex flex-wrap gap-4 text-sm text-purple-200">
|
||||||
<span className="prestige-count">Prestiges : {prestigeCount}</span>
|
<span>Générations : {prestigeCount}</span>
|
||||||
<span className="prestige-multiplier">
|
<span>Mult : x{prestigeMultiplier.toFixed(1)}</span>
|
||||||
Multiplicateur : ×{prestigeMultiplier.toFixed(1)}
|
<span>ADN : {ancestralDna}</span>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{canPrestige && (
|
{canPrestige && (
|
||||||
<div className="prestige-action">
|
<div className="flex flex-col gap-2 mt-2">
|
||||||
<div className="prestige-reward">
|
<p className="text-sm text-purple-100">
|
||||||
Récompense disponible : <strong>+0.1× multiplicateur permanent</strong>
|
Nouvelle Génération : <strong>+{dnaPreview} ADN</strong> + <strong>+0.1x mult</strong>
|
||||||
</div>
|
</p>
|
||||||
<button
|
<button
|
||||||
className="prestige-button"
|
|
||||||
onClick={handlePrestige}
|
onClick={handlePrestige}
|
||||||
aria-label="Déclencher le prestige"
|
className="px-4 py-2 rounded-lg bg-purple-600 hover:bg-purple-500 text-white font-semibold text-sm transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
Prestige
|
Nouvelle Génération
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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 (
|
|
||||||
<div className="divMagasinAmelio">
|
|
||||||
<h2>Magasin d'Améliorations</h2>
|
|
||||||
<div className="divAmelioActives">
|
|
||||||
<p>Améliorations Actives :</p>
|
|
||||||
{[1, 2, 3, 4].map((amount) => (
|
|
||||||
<div key={amount}>
|
|
||||||
Price: {activePrices[amount - 1]} - (+
|
|
||||||
{activeIncrementValues[amount - 1]})
|
|
||||||
<button
|
|
||||||
className="amelioActives"
|
|
||||||
onClick={() => acheterAmelioration("actif", amount)}
|
|
||||||
>
|
|
||||||
Acheter
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="divAmelioPassives">
|
|
||||||
<p>Améliorations Passives :</p>
|
|
||||||
{[1, 2, 3, 4].map((amount) => (
|
|
||||||
<div key={amount}>
|
|
||||||
Price: {passivePrices[amount - 1]} - (+
|
|
||||||
{passiveIncrementValues[amount - 1]})
|
|
||||||
<button
|
|
||||||
className="amelioPassives"
|
|
||||||
onClick={() => acheterAmelioration("passif", amount)}
|
|
||||||
>
|
|
||||||
Acheter
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Ameliorations;
|
|
||||||
@@ -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 (
|
|
||||||
<WildCoinContext.Provider value={value}>
|
|
||||||
{children}
|
|
||||||
</WildCoinContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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 (
|
|
||||||
<WildCoinContext.Provider value={contextValue}>
|
|
||||||
{children}
|
|
||||||
</WildCoinContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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 (
|
|
||||||
<img src={WildCoinS} className="wildCoinBtn" style={{width:"40px", height:"40px"}} alt="Clique pour augmenter le score" aria-label="Clique pour augmenter le score" onClick={handleIncrement} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default WildCoinIncrementAction;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { useWildCoin } from "../WildCoin/WildCoinContext";
|
|
||||||
|
|
||||||
function Timer() {
|
|
||||||
const { formatTime, seconds } = useWildCoin();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p>{formatTime(seconds)}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Timer;
|
|
||||||
@@ -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<GameState>(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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import AchievementsCard from "../components/AchievementsCard";
|
import AchievementsCard from "../components/AchievementsCard";
|
||||||
import "../scss/achievements.scss";
|
import "../scss/achievements.scss";
|
||||||
import { useWildCoin } from "../components/WildCoin/WildCoinContext";
|
import { useGameStore } from "../store/useGameStore";
|
||||||
import achievements from "../data/Achievements.json";
|
import achievements from "../data/Achievements.json";
|
||||||
|
|
||||||
function Achievements() {
|
function Achievements() {
|
||||||
const { wildCoin } = useWildCoin();
|
const resources = useGameStore((s) => s.state.resources);
|
||||||
let score = 1;
|
let score = 1;
|
||||||
if (wildCoin >= 25) {
|
if (resources >= 25) {
|
||||||
score = Math.floor((wildCoin - 25) / 400) + 1;
|
score = Math.floor((resources - 25) / 400) + 1;
|
||||||
} else {
|
} else {
|
||||||
score = 0;
|
score = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
146
Frontend/src/store/useGameStore.ts
Normal file
146
Frontend/src/store/useGameStore.ts
Normal file
@@ -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<GameStore>((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,
|
||||||
|
}));
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [tailwindcss(), react()],
|
||||||
test: {
|
test: {
|
||||||
environment: 'node',
|
environment: 'node',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user