diff --git a/Frontend/src/components/AchievementsCard.jsx b/Frontend/src/components/AchievementsCard.jsx
deleted file mode 100755
index c2ee1f3..0000000
--- a/Frontend/src/components/AchievementsCard.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import "../scss/components/achievementscard.scss";
-import PropTypes from "prop-types";
-
-function AchievementsCard({ name, description, image, key }) {
- AchievementsCard.propTypes = {
- name: PropTypes.string.isRequired,
- description: PropTypes.string.isRequired,
- image: PropTypes.string.isRequired,
- key: PropTypes.number.isRequired,
- };
-
- return (
-
-

-
-
{name}
-
{description}
-
-
- );
-}
-
-export default AchievementsCard;
diff --git a/Frontend/src/components/BoutiqueCard.jsx b/Frontend/src/components/BoutiqueCard.jsx
deleted file mode 100755
index 2548ad9..0000000
--- a/Frontend/src/components/BoutiqueCard.jsx
+++ /dev/null
@@ -1,65 +0,0 @@
-// BoutiqueCard.jsx — Legacy shop card (shop.json boosters)
-// TODO: Migrate to economy.ts generator system in a future step
-import "../scss/components/boutiquecard.scss";
-import "../scss/components/buttons.scss";
-import PropTypes from "prop-types";
-import { useGameStore } from "../store/useGameStore";
-
-export default function BoutiqueCard({
- name,
- price,
- incrementValue,
- description,
- image,
- type,
-}) {
- const resources = useGameStore((s) => s.state.resources);
-
- // Legacy shop — disabled for now, generators are in GeneratorShop
- const canAfford = resources >= price;
-
- return (
-
-
-
-
-
-
-
-
- {type} + {incrementValue}
-
-
-
{description}
-
-
-
-
-
- );
-}
-
-BoutiqueCard.propTypes = {
- name: PropTypes.string.isRequired,
- price: PropTypes.number.isRequired,
- incrementValue: PropTypes.number.isRequired,
- description: PropTypes.string.isRequired,
- image: PropTypes.string.isRequired,
- type: PropTypes.string.isRequired,
-};
diff --git a/Frontend/src/data/Achievements.json b/Frontend/src/data/Achievements.json
deleted file mode 100755
index be17fa6..0000000
--- a/Frontend/src/data/Achievements.json
+++ /dev/null
@@ -1,308 +0,0 @@
-[
- {
- "id": 1,
- "name": "Véritable cactus en peluche",
- "founded": false,
- "image": "https://i.goopics.net/n0tuti.jpg"
- },
- {
- "id": 2,
- "name": "Brosse à dents sans poil",
- "founded": false,
- "image": "https://i.goopics.net/hd42tk.jpg"
- },
- {
- "id": 3,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Un jour, Dark Vador s’est attaqué à Chuck Norris. Depuis, il fait de l’asthme."
- },
- {
- "id": 4,
- "name": "Photo d'Ayoub",
- "founded": false,
- "image": "https://i.goopics.net/fpanvh.jpg"
- },
- {
- "id": 5,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Pourquoi on met une selle sur un cheval ? Parce qu'en dessous, elle tomberait."
- },
- {
- "id": 6,
- "name": "Parapluie invisible",
- "founded": false,
- "image": "https://i.goopics.net/b8ms4o.jpg"
- },
- {
- "id": 7,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Qu'est-ce qui est plus merveilleux que de faire tourner un enfant sur un tourniquet ? L'arrêter avec une pelle."
- },
- {
- "id": 8,
- "name": "Savon qui gratte",
- "founded": false,
- "image": "https://i.goopics.net/oeefev.jpg"
- },
- {
- "id": 9,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Pourquoi les moutons aiment le chewing-gum ? Car c'est bon pour la laine."
- },
- {
- "id": 10,
- "name": "Moule à glaçons géant",
- "founded": false,
- "image": "https://i.goopics.net/2sdm53.png"
- },
- {
- "id": 11,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Quand Chuck Norris fait un programme, il installe les modules, code et vend le programme... ensuite il demande à quoi il doit servir."
- },
- {
- "id": 12,
- "name": "Casquette avec ventilateur intégré",
- "founded": false,
- "image": "https://i.goopics.net/wjhe5i.jpg"
- },
- {
- "id": 13,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Chuck Norris a invité Albert Einstein...à son dîner de cons."
- },
- {
- "id": 14,
- "name": "Game boy color de tonton",
- "founded": false,
- "image": "https://i.goopics.net/w82iwu.jpg"
- },
- {
- "id": 15,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Vous savez pourquoi les pets puent ? Pour que les sourds en profitent !"
- },
- {
- "id": 16,
- "name": "Mug moustache",
- "founded": false,
- "image": "https://i.goopics.net/csu9z1.jpg"
- },
- {
- "id": 17,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Qu'est ce qui est jaune et qui n'attend pas ? Un citron pressé"
- },
- {
- "id": 18,
- "name": "Peau de grenouille rare",
- "founded": false,
- "image": "https://i.goopics.net/uwjwn1.jpg"
- },
- {
- "id": 19,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Que met un développeur sur sa voiture en hiver ? Une bash."
- },
- {
- "id": 20,
- "name": "Stickers Gnia gnia gnia 5 minutes la présentation",
- "founded": false,
- "image": "https://i.goopics.net/8jids3.png"
- },
- {
- "id": 21,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Comment une blonde fait-elle pour faire un double de ses clefs ? Elle les photocopie."
- },
- {
- "id": 22,
- "name": "Photo de ta mamie...",
- "founded": false,
- "image": "https://i.goopics.net/htqzwa.jpg"
- },
- {
- "id": 23,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Qu'est-ce qu'un cochon volant ? Un aéroport."
- },
- {
- "id": 24,
- "name": "Livre de names de Tonton",
- "founded": false,
- "image": "https://i.goopics.net/avr4b4.png"
- },
- {
- "id": 25,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Mon grand-père avait prédit que le Titanic coulerait, il l'avait répété maintes fois...Mais on a préféré le virer de la salle de cinéma"
- },
- {
- "id": 26,
- "name": "Oreiller qui ronfle",
- "founded": false,
- "image": "https://i.goopics.net/4ydby9.png"
- },
- {
- "id": 27,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Un jour, les Power Rangers ont combattu Chuck Norris. Depuis, on les appelle les Teletubbies."
- },
- {
- "id": 28,
- "name": "Haut-parleur de douche non étanche",
- "founded": false,
- "image": "https://i.goopics.net/ugqwdj.jpg"
- },
- {
- "id": 29,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Qu'est-ce qui a deux pattes et qui saigne ? Un demi-chien..."
- },
- {
- "id": 30,
- "name": "Kit de survie du marais au wasabi et moutarde forte",
- "founded": false,
- "image": "https://i.goopics.net/dakyj9.png"
- },
- {
- "id": 31,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Sur quel site internet peut-on trouver un lave-vaisselle pas cher ? Meetic."
- },
- {
- "id": 32,
- "name": "Lunettes de soleil pour joueur de valorant",
- "founded": false,
- "image": "https://i.goopics.net/dxjicl.png"
- },
- {
- "id": 33,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Chuck Norris mine de la crypto-monnaie...avec la calculette de sa montre Casio"
- },
- {
- "id": 34,
- "name": "Parfum au PHP: aucune odeur",
- "founded": false,
- "image": "https://i.goopics.net/o2435t.png"
- },
- {
- "id": 35,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Quand Google ne trouve pas quelque chose, Il demande à Chuck Norris."
- },
- {
- "id": 36,
- "name": "Couronne de nénuphars lumineuse",
- "founded": false,
- "image": "https://i.goopics.net/d4su7e.png"
- },
- {
- "id": 37,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Quelles sont les choses les plus lourdes de l'univers ? Soleil, Étoiles, Trou noir...et node_modules..."
- },
- {
- "id": 38,
- "name": "Kit de survie pour la fin du monde",
- "founded": false,
- "image": "https://i.goopics.net/ltcik6.png"
- },
- {
- "id": 39,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Quelle est la fée la plus paresseuse ? La fée Néante"
- },
- {
- "id": 40,
- "name": "Bougie parfumée à l'essence de pizza",
- "founded": false,
- "image": "https://i.goopics.net/a3hv8n.jpg"
- },
- {
- "id": 41,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Que mettre dans un kit de survie pour la fin du monde ? Du chocolat, des cookies et un DVD de !"
- },
- {
- "id": 42,
- "name": "Le livre mein... craft",
- "founded": false,
- "image": "https://i.goopics.net/fuy8kq.png"
- },
- {
- "id": 43,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Un jour Hulk s’est battu contre Chuck Norris. Depuis, il fait de la pub pour du maïs."
- },
- {
- "id": 44,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Pourquoi les chaussettes ont-elles des orteils séparés ? Parce que même les pieds ont besoin d'intimité !"
- },
- {
- "id": 45,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "J'ai dit non ! Mon gynécologue m'a dit pas de sexe pendant 3 semaines. Et que t'as dit ton dentiste ?"
- },
- {
- "id": 46,
- "name": "Chaussettes avec orteils séparés",
- "founded": false,
- "image": "https://i.goopics.net/vn3xht.png"
- },
- {
- "id": 47,
- "name": "Blague:",
- "founded": false,
- "image": "https://images.pexels.com/photos/1115680/pexels-photo-1115680.jpeg",
- "description": "Quel est le jeu préféré des Portugais ?Call of d'outils"
- }
-]
diff --git a/Frontend/src/data/achievements.ts b/Frontend/src/data/achievements.ts
new file mode 100644
index 0000000..04a5e1b
--- /dev/null
+++ b/Frontend/src/data/achievements.ts
@@ -0,0 +1,216 @@
+// achievements.ts — Milestones Clickerz basés sur le GameState réel
+import { GameState } from "../core/economy";
+
+export interface Achievement {
+ id: string;
+ name: string;
+ description: string;
+ icon: string;
+ check: (state: GameState) => boolean;
+}
+
+function totalGeneratorsOwned(state: GameState): number {
+ return state.generators.reduce((sum, g) => sum + g.owned, 0);
+}
+
+function hasGenerator(state: GameState, genId: string): boolean {
+ return state.generators.some((g) => g.id === genId && g.owned > 0);
+}
+
+function hasEvolutionNode(state: GameState, nodeId: string): boolean {
+ return state.evolutionTree.some((n) => n.id === nodeId && n.unlocked);
+}
+
+export const ACHIEVEMENTS: Achievement[] = [
+ // --- Paliers de ressources ---
+ {
+ id: "first_tadpole",
+ name: "Premier Têtard",
+ description: "Faire éclore son tout premier têtard.",
+ icon: "🥚",
+ check: (s) => s.resources >= 1 || s.lifetimeTadpoles >= 1,
+ },
+ {
+ id: "colony",
+ name: "Colonie",
+ description: "Atteindre 100 têtards.",
+ icon: "🌱",
+ check: (s) => s.resources >= 100 || s.lifetimeTadpoles >= 100,
+ },
+ {
+ id: "village",
+ name: "Village",
+ description: "Atteindre 1 000 têtards.",
+ icon: "🏘️",
+ check: (s) => s.resources >= 1_000 || s.lifetimeTadpoles >= 1_000,
+ },
+ {
+ id: "city",
+ name: "Cité",
+ description: "Atteindre 10 000 têtards.",
+ icon: "🏙️",
+ check: (s) => s.resources >= 10_000 || s.lifetimeTadpoles >= 10_000,
+ },
+ {
+ id: "metropolis",
+ name: "Métropole",
+ description: "Atteindre 100 000 têtards.",
+ icon: "🌆",
+ check: (s) => s.resources >= 100_000 || s.lifetimeTadpoles >= 100_000,
+ },
+ {
+ id: "empire",
+ name: "Empire du Marais",
+ description: "Atteindre 1 000 000 de têtards.",
+ icon: "👑",
+ check: (s) => s.resources >= 1_000_000 || s.lifetimeTadpoles >= 1_000_000,
+ },
+
+ // --- Générateurs ---
+ {
+ id: "first_nid",
+ name: "Nidificateur",
+ description: "Construire son premier Nid.",
+ icon: "🪹",
+ check: (s) => hasGenerator(s, "nid"),
+ },
+ {
+ id: "first_mare",
+ name: "Batracien",
+ description: "Aménager sa première Mare.",
+ icon: "💧",
+ check: (s) => hasGenerator(s, "mare"),
+ },
+ {
+ id: "first_marecage",
+ name: "Marécageux",
+ description: "S'enfoncer dans son premier Marécage.",
+ icon: "🌿",
+ check: (s) => hasGenerator(s, "marecage"),
+ },
+ {
+ id: "first_etang",
+ name: "Gardien de l'Étang",
+ description: "Découvrir un Étang Ancien.",
+ icon: "🏛️",
+ check: (s) => hasGenerator(s, "etang"),
+ },
+ {
+ id: "first_lac",
+ name: "Seigneur du Lac",
+ description: "Accéder au Lac Mystique.",
+ icon: "🔮",
+ check: (s) => hasGenerator(s, "lac"),
+ },
+ {
+ id: "industriel",
+ name: "Industriel",
+ description: "Posséder 10 générateurs au total.",
+ icon: "🏭",
+ check: (s) => totalGeneratorsOwned(s) >= 10,
+ },
+ {
+ id: "magnate",
+ name: "Magnate",
+ description: "Posséder 50 générateurs au total.",
+ icon: "💎",
+ check: (s) => totalGeneratorsOwned(s) >= 50,
+ },
+ {
+ id: "tycoon",
+ name: "Tycoon du Marais",
+ description: "Posséder 100 générateurs au total.",
+ icon: "🐸",
+ check: (s) => totalGeneratorsOwned(s) >= 100,
+ },
+
+ // --- Prestige ---
+ {
+ id: "first_prestige",
+ name: "Nouvelle Génération",
+ description: "Effectuer son premier prestige.",
+ icon: "🧬",
+ check: (s) => s.prestigeCount >= 1,
+ },
+ {
+ id: "veteran",
+ name: "Vétéran",
+ description: "Atteindre 5 prestiges.",
+ icon: "⭐",
+ check: (s) => s.prestigeCount >= 5,
+ },
+ {
+ id: "legend",
+ name: "Légende du Marais",
+ description: "Atteindre 10 prestiges.",
+ icon: "🏆",
+ check: (s) => s.prestigeCount >= 10,
+ },
+
+ // --- ADN & Évolution ---
+ {
+ id: "first_dna",
+ name: "ADN Ancestral",
+ description: "Accumuler son premier ADN.",
+ icon: "🧪",
+ check: (s) => s.ancestralDna >= 1,
+ },
+ {
+ id: "first_evolution",
+ name: "Première Mutation",
+ description: "Débloquer la Ponte Améliorée.",
+ icon: "🦎",
+ check: (s) => hasEvolutionNode(s, "ponte_amelioree"),
+ },
+ {
+ id: "full_tree",
+ name: "Évolution Complète",
+ description: "Débloquer tous les noeuds de l'arbre.",
+ icon: "🌳",
+ check: (s) => s.evolutionTree.every((n) => n.unlocked),
+ },
+
+ // --- Easter eggs & humour ---
+ {
+ id: "chuck_norris",
+ name: "Blague",
+ description: "Quand Chuck Norris fait un programme, il installe les modules, code et vend le programme... ensuite il demande à quoi il doit servir.",
+ icon: "🤜",
+ check: (s) => s.lifetimeTadpoles >= 42,
+ },
+ {
+ id: "patience",
+ name: "Patience de Têtard",
+ description: "Un têtard ne devient pas grenouille en un jour. Toi non plus visiblement.",
+ icon: "🐌",
+ check: (s) => hasGenerator(s, "nid") && s.resources < 50,
+ },
+ {
+ id: "brain_powered",
+ name: "Powered by Brain",
+ description: "Le Brain a codé ce succès avant de savoir pourquoi. Classique.",
+ icon: "🧠",
+ check: (s) => s.prestigeCount >= 1 && totalGeneratorsOwned(s) >= 10,
+ },
+ {
+ id: "marecage_addict",
+ name: "Addict au Marécage",
+ description: "T'as 10 marécages. Tu sens un peu la vase mais on respecte l'engagement.",
+ icon: "🫠",
+ check: (s) => s.generators.find((g) => g.id === "marecage")?.owned >= 10,
+ },
+ {
+ id: "overkill",
+ name: "Overkill",
+ description: "25 Nids. T'aurais pu investir dans un Lac, mais non.",
+ icon: "😅",
+ check: (s) => s.generators.find((g) => g.id === "nid")?.owned >= 25,
+ },
+ {
+ id: "symbiose_joke",
+ name: "Le Cercle de la Vie",
+ description: "Symbiose activée. Même Mufasa serait fier.",
+ icon: "🦁",
+ check: (s) => hasEvolutionNode(s, "symbiose"),
+ },
+];
diff --git a/Frontend/src/data/shop.json b/Frontend/src/data/shop.json
deleted file mode 100755
index 495324b..0000000
--- a/Frontend/src/data/shop.json
+++ /dev/null
@@ -1,92 +0,0 @@
-[
- {
- "name": "Griffes de Grenouille",
- "price": 15,
- "incrementValue": 1,
- "description": "Des griffes acérées pour une ponte plus efficace. +1 par clic.",
- "link": "/",
- "image": "./svg/Hand.svg",
- "buyed": false,
- "type": "actif"
- },
- {
- "name": "Algues Nutritives",
- "price": 15,
- "incrementValue": 1,
- "description": "Les algues nourrissent le marais en continu. +1 têtard/s.",
- "link": "/",
- "image": "./svg/Tasse.svg",
- "buyed": false,
- "type": "passif"
- },
- {
- "name": "Crapaud Gardien",
- "price": 150,
- "incrementValue": 10,
- "description": "Un ancien du marais qui veille sur les pontes. +10 par clic.",
- "link": "/",
- "image": "./svg/Bonhome.svg",
- "buyed": false,
- "type": "actif"
- },
- {
- "name": "Nénuphar Géant",
- "price": 150,
- "incrementValue": 10,
- "description": "Un nénuphar massif qui attire les têtards. +10 têtards/s.",
- "link": "/",
- "image": "./svg/Bonnet.svg",
- "buyed": false,
- "type": "passif"
- },
- {
- "name": "Oeuf Doré",
- "price": 1500,
- "incrementValue": 100,
- "description": "Un oeuf rare qui éclot en masse. +100 par clic.",
- "link": "/",
- "image": "./svg/Cookie.svg",
- "buyed": false,
- "type": "actif"
- },
- {
- "name": "Mousse Lumineuse",
- "price": 1500,
- "incrementValue": 100,
- "description": "La mousse phosphorescente accélère la croissance. +100 têtards/s.",
- "link": "/",
- "image": "./svg/Canne.svg",
- "buyed": false,
- "type": "passif"
- },
- {
- "name": "Couronne de Roseaux",
- "price": 15000,
- "incrementValue": 1000,
- "description": "Le symbole du Gardien suprême du Marais. +1000 par clic.",
- "link": "/",
- "image": "./svg/Courone.svg",
- "buyed": false,
- "type": "actif"
- },
- {
- "name": "Esprit du Marais",
- "price": 15000,
- "incrementValue": 1000,
- "description": "L'esprit ancestral bénit les eaux. +1000 têtards/s.",
- "link": "/",
- "image": "./svg/PainDep.svg",
- "buyed": false,
- "type": "passif"
- },
- {
- "name": "Nectar de Lotus",
- "price": 8000,
- "incrementValue": 1000,
- "description": "Un nectar enivrant qui trouble les eaux... mais booste la ponte. Attention aux effets secondaires.",
- "link": "/",
- "image": "./svg/Beer.svg",
- "buyed": false,
- "type": "actif"
- }
-]
diff --git a/Frontend/src/main.jsx b/Frontend/src/main.jsx
index f99294f..d0bb50c 100755
--- a/Frontend/src/main.jsx
+++ b/Frontend/src/main.jsx
@@ -7,7 +7,6 @@ import ErrorPage from "./pages/404";
import Login from "./pages/Login";
import AuthCallback from "./pages/AuthCallback";
import { AuthProvider } from "./context/AuthContext";
-import Boutique from "./pages/Boutique";
import Achievements from "./pages/Achievements";
import Legal from "./pages/Legal";
import Cookie from "./pages/Cookie";
@@ -25,11 +24,7 @@ const router = createBrowserRouter([
path: "/jeu",
element: ,
},
- {
- path: "/boutique",
- element: ,
- },
- {
+{
path: "/achievements",
element: ,
},
diff --git a/Frontend/src/pages/Achievements.jsx b/Frontend/src/pages/Achievements.jsx
index 78e414a..8168641 100755
--- a/Frontend/src/pages/Achievements.jsx
+++ b/Frontend/src/pages/Achievements.jsx
@@ -1,37 +1,45 @@
-import { useState } from "react";
-import AchievementsCard from "../components/AchievementsCard";
-import "../scss/achievements.scss";
-import { useGameStore } from "../store/useGameStore";
-import achievements from "../data/Achievements.json";
-
-function Achievements() {
- const resources = useGameStore((s) => s.state.resources);
- let score = 1;
- if (resources >= 25) {
- score = Math.floor((resources - 25) / 400) + 1;
- } else {
- score = 0;
- }
- return (
-
-
Succès
-
-
- {achievements &&
- achievements.slice(0, score).map((a) => {
- return (
-
- );
- })}
-
-
-
- );
-}
-
-export default Achievements;
+import { useGameStore } from "../store/useGameStore";
+import { ACHIEVEMENTS } from "../data/achievements";
+import "../scss/achievements.scss";
+
+function Achievements() {
+ const state = useGameStore((s) => s.state);
+
+ const unlocked = ACHIEVEMENTS.filter((a) => a.check(state));
+ const locked = ACHIEVEMENTS.filter((a) => !a.check(state));
+
+ return (
+
+
Succès
+
+ {unlocked.length} / {ACHIEVEMENTS.length}
+
+
+
+
+ {unlocked.map((a) => (
+
+
{a.icon}
+
+
{a.name}
+
{a.description}
+
+
+ ))}
+
+ {locked.map((a) => (
+
+ ))}
+
+
+
+ );
+}
+
+export default Achievements;
diff --git a/Frontend/src/pages/Boutique.jsx b/Frontend/src/pages/Boutique.jsx
deleted file mode 100755
index 78bb908..0000000
--- a/Frontend/src/pages/Boutique.jsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import BoutiqueCard from "../components/BoutiqueCard";
-import "../scss/shop.scss";
-import shop from "../data/shop";
-
-export default function Boutique() {
-
-
- return (
-
-
-
Boutique
-
- {shop.map((item) => {
- return (
-
- );
- })}
-
-
-
- );
-}
diff --git a/Frontend/src/scss/achievements.scss b/Frontend/src/scss/achievements.scss
index c1884f8..2ac14ee 100755
--- a/Frontend/src/scss/achievements.scss
+++ b/Frontend/src/scss/achievements.scss
@@ -1,31 +1,98 @@
-.fullachieve {
- display: flex;
- flex-direction: column;
- padding-top: 10rem;
- background-color: var(--color-blue-light);
- width: 100%;
- max-width: 1280px;
- margin: 0 auto;
-}
-
-.achievementscontainer {
- margin: auto;
- display: flex;
- align-items: center;
-}
-
-h1 {
- text-align: center;
- font-family: var(--font);
- font-size: 3rem;
- color: var(--color-grey);
- margin-bottom: 3rem;
-}
-
-.achievementscardcontainer {
- display: flex;
- justify-content: center;
- flex-wrap: wrap;
- min-height: 300px;
- gap: 3rem;
-}
+.fullachieve {
+ display: flex;
+ flex-direction: column;
+ padding-top: 6rem;
+ padding-bottom: 3rem;
+ background-color: var(--color-blue-light);
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+ min-height: 80vh;
+
+ h1 {
+ text-align: center;
+ font-family: var(--font);
+ font-size: 2.5rem;
+ color: var(--color-grey);
+ margin-bottom: 0.5rem;
+ }
+}
+
+.achieve-counter {
+ text-align: center;
+ font-family: var(--font);
+ font-size: 1.1rem;
+ color: var(--color-grey);
+ opacity: 0.7;
+ margin-bottom: 2rem;
+}
+
+.achievementscontainer {
+ margin: auto;
+ display: flex;
+ align-items: center;
+ width: 100%;
+ padding: 0 2rem;
+}
+
+.achievementscardcontainer {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ min-height: 200px;
+ gap: 1rem;
+ width: 100%;
+}
+
+.achieve-card {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding: 1rem 1.2rem;
+ border-radius: 0.75rem;
+ width: 100%;
+ max-width: 380px;
+ transition: transform 0.15s ease;
+
+ &:hover {
+ transform: translateY(-2px);
+ }
+}
+
+.achieve-unlocked {
+ background: rgba(16, 185, 129, 0.12);
+ border: 1px solid rgba(16, 185, 129, 0.3);
+}
+
+.achieve-locked {
+ background: rgba(107, 114, 128, 0.08);
+ border: 1px solid rgba(107, 114, 128, 0.15);
+ opacity: 0.5;
+}
+
+.achieve-icon {
+ font-size: 2rem;
+ flex-shrink: 0;
+ width: 3rem;
+ text-align: center;
+}
+
+.achieve-info {
+ display: flex;
+ flex-direction: column;
+ gap: 0.2rem;
+}
+
+.achieve-name {
+ font-family: var(--font);
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--color-grey);
+}
+
+.achieve-desc {
+ font-family: var(--font);
+ font-size: 0.85rem;
+ color: var(--color-grey);
+ opacity: 0.7;
+}
diff --git a/Frontend/src/scss/components/achievementscard.scss b/Frontend/src/scss/components/achievementscard.scss
deleted file mode 100755
index eb39ff7..0000000
--- a/Frontend/src/scss/components/achievementscard.scss
+++ /dev/null
@@ -1,33 +0,0 @@
-.achievCardcontainer {
- display: flex;
- flex-direction: column;
- align-items: center;
- border: solid 0.05rem;
- max-width: 250px;
- border-radius: 1rem;
- background-color: rgb(255, 255, 255);
- margin-bottom: 1rem;
- box-shadow: 1px 1px 10px 2px var(--color-grey);
-
-}
-
-.achievecardpicture {
- width: 100%;
- border-radius: 1rem 1rem 0 0;
-}
-
-.achievname {
- font-size: 1.2rem;
- margin-top: 0.2rem;
- font-family: var(--font);
- text-align: center;
- color:rgb(29, 30, 30);
-}
-.achievdescription {
- font-size: 1rem;
- font-weight: 400;
- color: var(--color-grey);
- font-family: var(--font);
- color:rgb(25, 25, 26);
- padding: 1rem;
-}
diff --git a/Frontend/src/scss/components/boutiquecard.scss b/Frontend/src/scss/components/boutiquecard.scss
deleted file mode 100755
index 32b14ca..0000000
--- a/Frontend/src/scss/components/boutiquecard.scss
+++ /dev/null
@@ -1,76 +0,0 @@
-.shopcardcontainer {
- display: flex;
- flex-direction: column;
- width: 300px;
- min-height: 520px;
- padding: 1rem;
- border-radius: 1rem;
- align-items: center;
- box-sizing: border-box;
- background-color: var(--color-white);
-
- font-family: var(--font);
-
- .shopcontainer {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- gap: 1rem;
-
- .cardpicture {
- width: 100%;
- height: 300px;
- padding: 3rem;
- background-size: 50%;
- background-position: center;
- background-repeat: no-repeat;
- border-radius: 10px;
- background-color: var(--color-purple-light);
- box-sizing: border-box;
- }
-
- .titlesection {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- margin-top: 1rem;
-
- .itemname {
- font-size: 1.2rem;
- font-weight: 600;
- color: var(--color-grey);
- }
-
- .price {
- display: flex;
- gap: 0.2rem;
- font-size: 1rem;
- font-weight: 400;
- color: var(--color-grey);
- .itemprice {
- font-weight: 600;
- color: var(--color-red-light);
- }
- .priceicon {
- width: 22px;
- height: 22px;
- background-image: url("/svg/tadpole.svg");
- background-size: contain;
- background-position: center;
- background-repeat: no-repeat;
- }
- }
- }
-
- .description {
- font-size: 1rem;
- font-weight: 400;
- color: var(--color-grey);
- }
- }
- .buttoncard {
- width: 100%;
- }
-}
diff --git a/Frontend/src/scss/shop.scss b/Frontend/src/scss/shop.scss
deleted file mode 100755
index ea3bdc1..0000000
--- a/Frontend/src/scss/shop.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-
-.shoppagecontainer {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 2rem;
- padding: 12rem 0 4rem;
-
- h1 {
- font-family: var(--font);
- font-size: 2rem;
- font-weight: 600;
- color: var(--color-grey);
- }
- .cardcontainer {
- display: flex;
- flex-wrap: wrap;
- gap: 1rem;
- justify-content: center;
- max-width: 1280px;
-
- }
-
-
-
-}
\ No newline at end of file