feat: initial import — ClickerZ formation project (Express + React/Vite)

This commit is contained in:
2026-03-15 14:29:33 +01:00
commit 4e93753250
118 changed files with 71039 additions and 0 deletions

0
Frontend/src/pages/.gitkeep Executable file
View File

26
Frontend/src/pages/404.jsx Executable file
View File

@@ -0,0 +1,26 @@
import { Link } from "react-router-dom";
import "../scss/pages.scss";
import Lottie from "react-lottie-player";
import animation404 from "../data/404-animation.json";
export default function NotFound() {
return (
<section>
<div className="containererror">
<Lottie
loop
animationData={animation404}
play
style={{ width: 260, height: 150 }}
/>
<h1>Oops! Il semble que la page n'existe pas.</h1>
<p className="message">
Nous vous conseillons de retourner à la page d'accueil.
</p>
<Link className="btn-return" to="/">
retourner à la page d'accueil
</Link>
</div>
</section>
);
}

View File

@@ -0,0 +1,37 @@
import { useState } from "react";
import AchievementsCard from "../components/AchievementsCard";
import "../scss/achievements.scss";
import { useWildCoin } from "../components/WildCoin/WildCoinContext";
import achievements from "../data/Achievements.json";
function Achievements() {
const { wildCoin } = useWildCoin();
let score = 1;
if (wildCoin >= 25) {
score = Math.floor((wildCoin - 25) / 400) + 1;
} else {
score = 0;
}
return (
<div className="fullachieve">
<h1>Succès</h1>
<div className="achievementscontainer">
<div className="achievementscardcontainer">
{achievements &&
achievements.slice(0, score).map((a) => {
return (
<AchievementsCard
key={a.id}
name={a.name}
description={a.description}
image={a.image}
/>
);
})}
</div>
</div>
</div>
);
}
export default Achievements;

33
Frontend/src/pages/Boutique.jsx Executable file
View File

@@ -0,0 +1,33 @@
import BoutiqueCard from "../components/BoutiqueCard";
// import { useWildCoin } from "..components/WildCoin/WildCoinContext";
import "../scss/shop.scss";
import shop from "../data/shop";
export default function Boutique() {
return (
<main>
<div className="shoppagecontainer">
<h1>Boutique</h1>
<div className="cardcontainer">
{shop.map((item) => {
return (
<BoutiqueCard
key={item.name}
name={item.name}
price={item.price}
incrementValue={item.incrementValue}
description={item.description}
image={item.image}
link={item.link}
type={item.type}
buyed={item.buyed}
/>
);
})}
</div>
</div>
</main>
);
}

100
Frontend/src/pages/Cookie.jsx Executable file
View File

@@ -0,0 +1,100 @@
import "../scss/Cookie.scss";
function Cookie() {
return (
<div className="container">
<div className="item">
<h2>Quest-ce quun cookie ?</h2>
<p>
Un cookie est un petit fichier texte sauvegardé sur votre ordinateur
lorsque vous visitez un site web. Ce fichier texte enregistre des
informations qui peuvent être lues par un site web lorsque vous le
visitez de nouveau plus tard. Certains de ces cookies sont nécessaires
pour accéder à certaines fonctionnalités dun site. Dautres cookies
sont dutilité pratique pour le visiteur : ils sauvegardent de manière
sécurisée votre nom dutilisateur ou vos préférences linguistiques par
exemple. Les cookies signifient tout simplement quà chaque fois que
vous visitez un site web, vous navez pas besoin de saisir à nouveau
les mêmes informations.
</p>
</div>
<div className="item">
<h2>Pourquoi Xmass Clicker utilise des cookies ?</h2>
<p>
Nous utilisons des cookies pour vous fournir une expérience
utilisateur optimale et adaptée à vos préférences personnelles. En
utilisant les cookies, Les cookies sont également utilisés pour
optimiser la performance du site. Xmass Clicker a pris toutes les
mesures organisationnelles et techniques pour protéger vos données
personnelles ainsi que dune éventuelle perte dinformations ou de
toute forme de traitement illicite. Pour davantage dinformations,
consultez notre Politique de confidentialité.
</p>
</div>
<div className="item">
<h2>Comment puis-je désactiver les cookies ?</h2>
<p>
Vous pouvez paramétrer votre navigateur Internet pour désactiver les
cookies. Notez toutefois que si vous désactivez les cookies, votre nom
dutilisateur ainsi que votre mot de passe ne seront plus sauvegardés
sur aucun site web.
</p>
</div>
<div className="item">
<h2>Firefox :</h2>
<p>
1. Ouvrez Firefox <br />
2. Appuyez sur la touche « Alt » <br />
3. Dans le menu en haut de la page cliquez sur « Outils » puis «
Options » <br />
4. Sélectionnez longlet « Vie privée » <br />
5. Dans le menu déroulant à droite de « Règles de conservation »,
cliquez sur « utiliser les paramètres personnalisés pour lhistorique
» <br />
6. Un peu plus bas, décochez « Accepter les cookies » <br />
7. Sauvegardez vos préférences en cliquant sur « OK »
</p>
</div>
<div className="item">
<h2>Internet Explorer/Edge :</h2>
<p>
1. Ouvrez Internet Explorer <br />
2. Dans le menu « Outils », sélectionnez « Options Internet » <br />
3. Cliquez sur longlet « Confidentialité » <br />
4. Cliquez sur « Avancé » et décochez « Accepter » <br />
5. Sauvegardez vos préférences en cliquant sur « OK »
</p>
</div>
<div className="item">
<h2>Safari :</h2>
<p>
1. Ouvrez Safari <br />
2. Dans la barre de menu en haut, cliquez sur « Safari », puis «
Préférences » <br />
3. Sélectionnez licône « Sécurité » <br />
4. À côté de « Accepter les cookies », cochez « Jamais » <br />
5. Si vous souhaitez voir les cookies qui sont déjà sauvegardés sur
votre ordinateur, cliquez sur « Afficher les cookies »
</p>
</div>
<div className="item">
<h2>Google Chrome :</h2>
<p>
1. Ouvrez Google Chrome <br />
2. Cliquez sur licône doutils dans la barre de menu <br />
3. Sélectionnez « Options » <br />
4. Cliquez sur longlet « Options avancées » <br />
5. Dans le menu déroulant « Paramètres des cookies », sélectionnez «
Bloquer tous les cookies »
</p>
</div>
</div>
);
}
export default Cookie;

231
Frontend/src/pages/Home.jsx Executable file
View File

@@ -0,0 +1,231 @@
import { Helmet } from "react-helmet";
import { useOutletContext } from "react-router-dom";
import PropTypes from "prop-types";
import "../scss/home.scss";
import { useEffect } from "react";
import { useWildCoin } from "../components/WildCoin/WildCoinContext";
export default function Home() {
const [toggleSnow, setToggleSnow] = useOutletContext();
Home.propTypes = {
setToggleSnow: PropTypes.function,
toggleSnow: PropTypes.bool,
}.isRequired;
const { biere, setBiere, santaDrunk, setSantaDrunk } = useWildCoin();
var snow = {
wind: 0,
maxXrange: 40,
minXrange: 20,
maxSpeed: 1,
minSpeed: 3,
color: "#fff",
char: "*",
maxSize: 32,
minSize: 10,
flakes: [],
WIDTH: -10,
HEIGHT: 0,
init: function (nb) {
var o = this,
frag = document.createDocumentFragment();
o.getSize();
for (var i = 0; i < nb; i++) {
var flake = {
x: o.random(o.WIDTH),
y: -o.maxSize,
xrange: o.minXrange + o.random(o.maxXrange - o.minXrange),
yspeed: o.minSpeed + o.random(o.maxSpeed - o.minSpeed, 100),
life: 0,
size: o.minSize + o.random(o.maxSize - o.minSize),
html: document.createElement("span"),
};
flake.html.style.position = "absolute";
flake.html.style.top = flake.y + "px";
flake.html.style.left = flake.x + "px";
flake.html.style.fontSize = flake.size + "px";
flake.html.style.color = o.color;
flake.html.appendChild(document.createTextNode(o.char));
frag.appendChild(flake.html);
flake.html.style.userSelect = "none";
flake.html.style.overflow = "hidden";
o.flakes.push(flake);
}
document.body.appendChild(frag);
o.animate();
window.onresize = function () {
o.getSize();
};
},
animate: function () {
var o = this;
for (var i = 0, c = o.flakes.length; i < c; i++) {
var flake = o.flakes[i],
top = flake.y + flake.yspeed,
left = flake.x + Math.sin(flake.life) * flake.xrange + o.wind;
if (
top < o.HEIGHT - flake.size - 10 &&
left < o.WIDTH - flake.size &&
left > 0
) {
flake.html.style.top = top + "px";
flake.html.style.left = left + "px";
flake.y = top;
flake.x += o.wind;
flake.life += 0.01;
} else {
flake.html.style.top = -o.maxSize + "px";
flake.x = o.random(o.WIDTH);
flake.y = -o.maxSize;
flake.html.style.left = flake.x + "px";
flake.life = 0;
}
}
setTimeout(function () {
o.animate();
}, 20);
},
stop: function () {
for (var i = 0, c = this.flakes.length; i < c; i++) {
document.body.removeChild(this.flakes[i].html);
}
this.flakes = [];
},
random: function (range, num) {
num = num ? num : 1;
return Math.floor(Math.random() * (range + 1) * num) / num;
},
getSize: function () {
this.WIDTH = document.body.clientWidth || window.innerWidth;
this.HEIGHT = document.body.clientHeight || window.innerHeight;
},
};
const { incrementClick, incrementWildCoin } = useWildCoin();
const createParticle = (x, y) => {
const cookieClicks = document.querySelector(".pieces");
const particle = document.createElement("a");
particle.style.backgroundImage = "url('/png/w-coin.png')";
particle.setAttribute("class", "pieces-particle");
particle.style.left = x + "%";
particle.style.bottom = y + "px";
cookieClicks.appendChild(particle);
setTimeout(() => {
cookieClicks.removeChild(particle);
}, 1500);
};
const handleIncrement = () => {
incrementWildCoin(incrementClick);
createParticle(50, 300);
};
useEffect(() => {
if (toggleSnow) {
snow.init(10);
} else {
snow.stop();
}
return () => {
snow.stop();
};
}, [toggleSnow]);
useEffect(() => {
const main = document.querySelector(".bghomecover");
const santa = document.querySelector(".santaclaus");
if (main !== undefined) {
if (biere[1] >= 1) {
santa.style.background = `url("/svg/SantaClause-drink.svg")`;
if (santaDrunk === true) {
main.style.filter = `blur(${biere[1]}px)`;
setTimeout(() => {
console.count("setTimeOut");
main.style.filter = `blur(0px)`;
setSantaDrunk(false);
}, biere[1] * 5000);
}
}
}
}, [biere, setBiere]);
return (
<main className="bghomecover">
<Helmet>
<meta
name="description"
content="Xmass Click votre nouveau Clicker préféré !"
/>
<meta name="robots" content="index, follow" />
<meta
name="googlebot"
content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"
/>
<meta
name="bingbot"
content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"
/>
<link rel="canonical" href="https://xmass.click/" />
<meta property="og:url" content="https://xmass.click/" />
<meta property="og:site_name" content="Xmass Click" />
<meta property="og:locale" content="fr_FR" />
<meta property="og:type" content="website" />
<meta property="og:title" content="mywebsite | title" />
<meta
property="og:description"
content="Xmass Click votre nouveau Clicker préféré !"
/>
<meta
property="og:image"
content="https://xmass.click/webp/share-cover.webp"
/>
<meta
property="og:image:secure_url"
content="https://xmass.click/webp/share-cover.webp"
/>
<meta property="og:image:width" content="584" />
<meta property="og:image:height" content="384" />
<meta property="fb:pages" content="" />
<meta property="fb:admins" content="" />
<meta property="fb:app_id" content="" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="" />
<meta name="twitter:creator" content="" />
<meta name="twitter:title" content="Xmass Click" />
<meta
name="twitter:description"
content="Xmass Click votre nouveau Clicker préféré !"
/>
<meta
name="twitter:image"
content="https://xmass.click/webp/share-cover.webp"
/>
<title>Xmass Click</title>
</Helmet>
<div className="santaposition">
<div className="pieces" />
<div className="santaclaus" onClick={handleIncrement} />
</div>
<div className="boostList"></div>
</main>
);
}

53
Frontend/src/pages/Legal.jsx Executable file
View File

@@ -0,0 +1,53 @@
import "../scss/Legal.scss";
function Legal() {
return (
<div className="mentionslegales">
<h2>Éditeur :</h2>
<p>
Xmass'Click est un projet réalisé dans le cadre d'un hackathon sur 2
jours.
</p>
<h2>Coordonnées :</h2>
<p>
Téléphone : 04 22 52 10 10 <br />
E-mail : pere-noel@laposte.net <br />
Adresse : 250 avenue des Nuages, 1000 Pôle Nord <br />
</p>
<h2>Responsabilité :</h2>
<p>
Xmass'Click décline toute responsabilité quant à l'utilisation du site.
Les informations fournies sont à titre informatif et peuvent être
sujettes à des erreurs.
</p>
<h2>Propriété Intellectuelle :</h2>
<p>
Tout le contenu du site (textes, images, etc.) reste la propriété de
Xmass'Click. Toute reproduction est interdite sans autorisation
préalable.
</p>
<h2>Protection des Données Personnelles :</h2>
<p>
Xmass'Click ne collecte pas de données personnelles. Aucune information
personnelle n'est stockée lors de l'utilisation du site.
</p>
<h2>Conditions Générales d'Utilisation :</h2>
<p>
Aucune condition générale d'utilisation n'est applicable. L'utilisation
du site Xmass'Click se fait à titre gratuit et sans engagement.
</p>
<h2>Loi Applicable :</h2>
<p>
Le présent site est régi par la loi du Pôle Nord. En cas de litige, les
tribunaux du Père Noël seront compétents.
</p>
</div>
);
}
export default Legal;