const { useState, useEffect } = React; const niveauxParMatiere = { maths: 0, chimie: 13, physique:5, geographie:1,biologie:15,geographie:8,zoologie:6,svt:12,Economie:3, Chimie :3,Anglais:10,BaccSvt:17,Vitamine:4,geologie:9,sciences_sociales:21, sport:7}; const classes = ["9eme", "NSIV","NS3","Sciences","NSI","Linguistique","Sport"]; const matieres = {"9eme": ["biologie","physique","sciences_sociales"], "NSIV": ["chimie","svt","Economie","BaccSvt","Vitamine"], "NS3":["chimie"],"Sciences":["geographie","zoologie","botanique","geologie"],"NSI":["Chimie"],"Linguistique":["Anglais"],"Sport":["sport"]}; const getUserProgress = () => {return JSON.parse(localStorage.getItem("userProgress") || "{}");}; const Loader = () => (

Chargement du quiz...

); function sauvegarderProgression(userId, classe, matiere, niveau) { fetch("api/saveProgress.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId, classe, matiere, niveau }) }) .then(res => res.json()) .then(data => { if (data.success) { console.log("✅ Progression enregistrée"); } else { console.error("⚠️ Erreur de sauvegarde :", data.message); } }) .catch(console.error); } function CookieBanner() { const [showBanner, setShowBanner] = React.useState(false); React.useEffect(() => { const consent = window.cookieUtils.get("cookieConsent"); if (!consent) setShowBanner(true); }, []); function acceptCookies() { window.cookieUtils.set("cookieConsent", "accepted", 365); setShowBanner(false); } function refuseCookies() { window.cookieUtils.set("cookieConsent", "refused", 365); setShowBanner(false); } if (!showBanner) return null; return React.createElement( "div", { style: { position: "fixed", bottom: "0", left: "0", width: "100%", backgroundColor: "#1e1e1e", color: "white", padding: "1rem", zIndex: 1000, display: "flex", flexDirection: "column", alignItems: "center", gap: "0.5rem", boxShadow: "0 -2px 10px rgba(0,0,0,0.3)", fontSize: "1rem" } }, React.createElement("div", { style: { maxWidth: "90%", textAlign: "center" } }, "Nous utilisons des cookies pour améliorer votre expérience. et pour diffuser des publicités. Acceptez-vous ?"), React.createElement("div", { style: { display: "flex", gap: "1rem", flexWrap: "wrap", justifyContent: "center" } }, React.createElement("button", { onClick: acceptCookies, style: { backgroundColor: "#4caf50", color: "white", border: "none", borderRadius: "5px", padding: "0.5rem 1.2rem", cursor: "pointer" } }, "Accepter"), React.createElement("button", { onClick: refuseCookies, style: { backgroundColor: "#f44336", color: "white", border: "none", borderRadius: "5px", padding: "0.5rem 1.2rem", cursor: "pointer" } }, "Refuser") ) ); } const MonetagRewardAd = ({ onRewarded }) => { React.useEffect(() => {const script = document.createElement('script'); script.src = 'https://groleegni.net/401/8727837'; script.async = true; try { (document.body || document.documentElement).appendChild(script); } catch (e) { console.error("Erreur lors de l'ajout du script Monetag :", e); } // Simulation durée pub puis récompense (ajuste si API dispo) const timer = setTimeout(() => { onRewarded(); }, 5000); return () => { clearTimeout(timer); if (script.parentNode) { script.parentNode.removeChild(script); } }; }, [onRewarded]); return (
en cours...
); }; const ShareButton = ({ url = window.location.href, text = "Essaie ce super quiz !" }) => { const [message, setMessage] = React.useState(""); const share = async () => { if (navigator.share) { try { await navigator.share({ title: "Quiz éducatif", text, url }); setMessage("✅ Partagé avec succès !"); } catch (err) { setMessage("❌ Échec du partage."); } } else { try { await navigator.clipboard.writeText(url); setMessage("✅ Lien copié dans le presse-papiers !"); } catch (e) { setMessage("❌ Impossible de copier le lien."); } } setTimeout(() => setMessage(""), 3000); }; return (
{message &&

{message}

}
); }; const AdsterraBanner = () => { React.useEffect(() => { const script = document.createElement('script'); script.src = '//pl25317511.profitableratecpm.com/3a/47/d3/3a47d34d1ef582ce3780ed350b61f84d.js'; script.async = true; try { (document.body || document.documentElement).appendChild(script); } catch (e) { console.error("Erreur lors de l'ajout du script Monetag :", e); } }, []); return (
{/* Ici iframe ou script Adsterra */} Bannière Adsterra
); }; const OnClickARewardedVideo = ({ onRewarded, onCancel }) => { const [canCancel, setCanCancel] = React.useState(false); React.useEffect(() => { // Injecter le script OnClickA const script = document.createElement("script"); script.src = "https://js.onclckmn.com/static/onclicka.js"; script.async = true; script.setAttribute("data-admpid", "276511"); document.body.appendChild(script); // Autoriser annulation après 15 sec const cancelTimer = setTimeout(() => { setCanCancel(true); }, 15000); const finishTimer = setTimeout(() => { onRewarded(); }, 15000); // Nettoyer au démontage return () => { clearTimeout(cancelTimer); clearTimeout(finishTimer); document.body.removeChild(script); }; }, []); return (
en cours...

🎬 en cours...

{canCancel && }
); }; function About() { const [principes, setPrincipes] = useState(null); useEffect(() => { fetch("/principes_jeu.json") .then(res => res.json()) .then(data => setPrincipes(data)); }, []); if (!principes) return <> ; return (

{principes.title}

Dernière mise à jour : {principes.lastUpdate}

{principes.sections.map((section, i) => (

{section.heading}

    {section.content.map((item, j) => (
  • {item}
  • ))}
))}
); } const Updat_bagde = async ()=>{ const ph=JSON.parse(localStorage.getItem("quizUser")); const c=JSON.parse(localStorage.getItem("badges")); if (c && ph) { const xhr = new XMLHttpRequest(); xhr.open("POST", "api/updateUserProgress.php", true); xhr.setRequestHeader("Content-Type", "application/json");xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { console.log("Réponse du serveur :", xhr.responseText); } }; xhr.send(JSON.stringify({or:c.or, phone:ph.phone, agent:c.argent,bronze:c.bronze, diamant:c.diamant})); } } const User_if = async ({pseudo, phone, password})=>{ const xhr = new XMLHttpRequest(); xhr.open("POST", "api/registerUser.php", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { console.log("Réponse du serveur :", xhr.responseText); } }; xhr.send(JSON.stringify({username:pseudo, phone:phone ,pass:password })); } const Udat_user = async ({phone,password})=>{ const reponse = await fetch("api/user_htjggfs.php",{ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({phone: phone, pass:password}) }); if(reponse.ok==true &&reponse.status==200){ const inf = await reponse.json(); return inf; }else{ return false; } } const Quiz = ({ questions, onFinish }) => { const [shuffledQuestions, setShuffledQuestions] = React.useState([]); const [index, setIndex] = React.useState(0); const [score, setScore] = React.useState(0); const [selected, setSelected] = React.useState(null); const [showFeedback, setShowFeedback] = React.useState(false); const [timeLeft, setTimeLeft] = React.useState(30); const [animate, setAnimate] = React.useState(true); const [fillInputs, setFillInputs] = React.useState([]); const [assocAnswers, setAssocAnswers] = React.useState({}); const current = questions[index]; const correctSound = React.useRef(null); const wrongSound = React.useRef(null); const beepSound = new Audio("image/sispens.mp3"); React.useEffect(() => { correctSound.current = new Audio("image/bon.mp3"); wrongSound.current = new Audio("image/erreur.wav"); }, []); const shuffleArray = (arr) => arr.map(a => ({ sort: Math.random(), value: a })) .sort((a, b) => a.sort - b.sort) .map(a => a.value); React.useEffect(() => { const shuffled = shuffleArray(questions).map(q => { const type = q.type || "choice"; if (type === "choice" || !q.type) { const rep = q.reponse || q.choices || q.options; const shuffledRep = shuffleArray(rep); return { ...q, type: "choice", reponse: shuffledRep, answer: typeof q.answer === "string" ? shuffledRep.indexOf(q.answer) : q.answer }; } return q; }); setShuffledQuestions(shuffled); }, [questions]); const question = shuffledQuestions[index]; React.useEffect(() => { if (!question || selected !== null || question.type === "association") return; const timer = setInterval(() => { setTimeLeft((prev) => { if (prev <= 8 && prev > 1) beepSound.play().catch(() => {}); if (prev === 1) { clearInterval(timer); handleAnswer(null); return 0; } return prev - 1; }); }, 1000); return () => clearInterval(timer); }, [index, selected, question]); const handleAnswer = (i) => { if (selected !== null) return; const isCorrect = i !== null && i === question.answer; setSelected(i); setShowFeedback(true); if (isCorrect) { correctSound.current?.play(); setScore(s => s + 1); } else { wrongSound.current?.play(); } setTimeout(() => nextQuestion(isCorrect), 5000); }; const handleFillSubmit = () => { const isCorrect = JSON.stringify(fillInputs.map(t => t.trim().toLowerCase())) === JSON.stringify(question.answers.map(t => t.trim().toLowerCase())); setSelected(true); setShowFeedback(true); if (isCorrect) { correctSound.current?.play(); setScore(s => s + 1); } else { wrongSound.current?.play(); } setTimeout(() => nextQuestion(isCorrect), 8000); }; const RenderFeedback = () => { if (!showFeedback) return null; const type = question.type || "choice"; const feedback = question.feedback || ""; switch (type) { case "choice": return

💡 {feedback || "Cette réponse était informative."}

; case "fill": return (

📝 Réponse attendue : {question.answers.join(" / ")}

{feedback && typeof feedback === "string" &&

💬 {feedback}

}
); case "association": return (

✔️ Bonnes correspondances :

); default: return

💬 {typeof feedback === "string" ? feedback : "Aucune explication fournie."}

; } }; const handleAssociationSubmit = () => { const correct = question.pairs.every(p => assocAnswers[p.label] === question.answers[p.label]); setSelected(true); setShowFeedback(true); if (correct) { correctSound.current?.play(); setScore(s => s + 1); } else { wrongSound.current?.play(); } setTimeout(() => nextQuestion(correct), 15000); }; const nextQuestion = (wasCorrect) => { const finalScore = score + (wasCorrect ? 1 : 0); if (index + 1 < shuffledQuestions.length) { setAnimate(false); setTimeout(() => { setIndex(i => i + 1); setSelected(null); setShowFeedback(false); setTimeLeft(30); setFillInputs([]); setAssocAnswers({}); setAnimate(true); }, 50); } else { onFinish(finalScore, shuffledQuestions.length); } }; if (!question) return
Chargement…
; return (
{timeLeft}s

🧠 Score actuel : {score} / {questions.length}

Question {index + 1} / {shuffledQuestions.length}

{question.type === "choice" && (
{question.reponse.map((opt, i) => { let btnClass = "option-btn"; if (selected !== null) { if (i === question.answer) btnClass += " correct"; else if (i === selected) btnClass += " wrong"; } return (
)} {question.type === "fill" && (

{question.answers.map((_, i) => ( { const newInputs = [...fillInputs]; newInputs[i] = e.target.value; setFillInputs(newInputs); }} placeholder={`Réponse ${i + 1}`} /> ))}

)} {question.type === "association" && (
{question.pairs.map((pair) => (
{pair.label}. {pair.text}
))}
)} {RenderFeedback() }

); } const App = () => { const [currentTab, setCurrentTab] = useState("quiz"); const [classe, setClasse] = useState(null); const [matiere, setMatiere] = useState(null); const [showExtraNiveaux, setShowExtraNiveaux] = useState(false); const [niveauActif, setNiveauActif] = useState(null); const [questions, setQuestions] = useState(null); const [loading, setLoading] = useState(false); const [showDashboard, setshowDashboard] = useState(false); const [onComplete, setonComplete] = useState(false); const [userData, setUserData] = useState(() => JSON.parse(localStorage.getItem("userData")) || {}); const [modal, setModal] = useState(null); const [isReplay, setIsReplay] = useState(false); const [userProfile, setUserProfile] = React.useState(null); const [showOnClickA, setShowOnClickA] = useState(false); const [showMonetag, setShowMonetag] = useState(false); const [pendingNiveau, setPendingNiveau] = useState(null); // niveau à lancer après pub const [replayAfterAd, setReplayAfterAd] = useState(false); const [showMiniQuiz, setShowMiniQuiz] = useState(true); React.useEffect(() => { const saved = localStorage.getItem("userProfile"); if (saved) { setUserProfile(JSON.parse(saved)); }}, []); const handleSelectClasse = (c) => { setClasse(c); setMatiere(null); setNiveauActif(null); }; const handleSelectMatiere = (m) => { setMatiere(m); setNiveauActif(null); }; const handleStartNiveau = (n) => { const key = `${classe}_${matiere}_niveau${n}`; const niveauData = userData[key] || { completed: false, attempts: 0 }; if (niveauData.completed) { // niveau déjà fait => proposer rejouer avec pub ou points return setModal({ title: "Rejouer ce niveau ?", content: "Ce niveau est terminé. Pour rejouer, choisissez une option :", actions: [ { label: "🎥 Regarder pub OnClickA", onClick: () => { setPendingNiveau(key); setReplayAfterAd(true); setShowOnClickA(true); setModal(null); } }, { label: "🎥 Regarder pub Monetag", onClick: () => { setPendingNiveau(key); setReplayAfterAd(true); setShowMonetag(true); setModal(null); } }, { label: "💰 Dépenser 20 points", onClick: () => spendPoints(key) }, { label: "Annuler", onClick: () => setModal(null) } ] }); } // Niveau jamais fait => lancement normal (optionnel pub Monetag avant) setPendingNiveau(key); setReplayAfterAd(false); setShowMonetag(true); // ou setShowOnClickA(true); }; const allowReplay = (key) => { setModal(null); startNiveau(key, true); }; const spendPoints = (key) => { const points = parseInt(localStorage.getItem("totalPoints") || "0"); if (points >= 20) { localStorage.setItem("totalPoints", points - 30); allowReplay(key); } else { setModal({ title: "Pas assez de points", content: "Vous avez besoin de 30 points pour rejouer ce niveau.", actions: [{ label: "Fermer", onClick: () => setModal(null) }] });}}; let lastNiveau=null; const startNiveau = (key, replay) => { if(!localStorage.getItem("lastNiveauKey")){ localStorage.setItem("lastNiveauKey", JSON.stringify({})) } lastNiveau=JSON.parse(localStorage.getItem("lastNiveauKey")); const info=(key.split("_")); if(info[1]=="chimie" && info[0]=="NSIV"){ lastNiveau.chimieNSIV=key }else if(info[1]=="biologie" && info[0]=="9eme"){ lastNiveau.bio9eme=key; } localStorage.setItem("lastNiveauKey",JSON.stringify(lastNiveau)); setIsReplay(replay); setLoading(true); fetch(`json/${key}.json`) .then(res => res.json()) .then(data => { setQuestions(data.questions); setNiveauActif(key); }) .catch(() => { setModal({ title: "Erreur", content: "Impossible de charger les questions.", actions: [{ label: "Fermer", onClick: () => setModal(null) }] }); }) .finally(() => setLoading(false)); }; if(! localStorage.getItem("badges")){ localStorage.setItem("badges",JSON.stringify({argent:0, or:0,bronze:0, diamant:0}));} const handleFinish = (score, total) => { const key = niveauActif; // exemple: "NSIV_Chimie_niveau3" const prevData = userData[key] || { attempts: 0, completed: false }; const newData = { ...userData, [key]: { completed: true, attempts: prevData.attempts + 1 } }; setUserData(newData); localStorage.setItem("userData", JSON.stringify(newData)); // 🔍 Extraire classe, matière, niveau depuis la clé const [classe, matiere, niveauStr] = key.split("_"); const niveau = parseInt(niveauStr.replace("niveau", "")); // 🔒 ID utilisateur (localStorage ou global context) const User =localStorage.getItem("quizUser") || null ; if(User){ const user = JSON.parse(localStorage.getItem("quizUser")); if (user?.id) { fetch("api/saveProgressUser.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId: user.id, classe, matiere, niveau }) }) .then(res => res.json()) .then(data => { if (!data.success) console.error("Erreur MySQL:", data.message); }) .catch(console.error); } } // 🏅 Attribution de badge if (!isReplay && prevData.attempts === 0) { const cbadges = JSON.parse(localStorage.getItem("badges")); let earnBagdes = null; let badge = null; let gbadge = 0; const pourcentage = (score / total) * 100; if (pourcentage === 100) { badge = "💎 Diamant"; gbadge = parseInt(cbadges.diamant || 0); cbadges.diamant = gbadge + 1; earnBagdes = 1; } else if (pourcentage >= 80 && pourcentage > 70) { badge = "🥇 Or"; gbadge = parseInt(cbadges.or || 0); cbadges.or = gbadge + 1; earnBagdes = 1; } else if (pourcentage >= 60 && pourcentage > 50) { badge = "🥈 Argent"; gbadge = parseInt(cbadges.argent || 0); cbadges.argent = gbadge + 1; earnBagdes = 1; } else if (pourcentage >= 50 && pourcentage < 60) { badge = "🥉 Bronze"; gbadge = parseInt(cbadges.bronze || 0); cbadges.bronze = gbadge + 1; earnBagdes = 1; } if (badge) { localStorage.setItem("badges", JSON.stringify(cbadges)); Updat_bagde(); setModal({ title: "🎉 Félicitations !", content: `Vous avez obtenu ${earnBagdes} badges ${badge}`, actions: [{ label: "Fermer", onClick: () => setModal(null) }] }); } } // ➕ Mise à jour des points const points = parseInt(localStorage.getItem("totalPoints") || "0") + score; localStorage.setItem("totalPoints", points); // Nettoyage final setQuestions(null); setNiveauActif(); setIsReplay(false); }; const niveaux = matiere ? [...Array(niveauxParMatiere[matiere]).keys()].map(i => { const key = `${classe}_${matiere}_niveau${i + 1}`; const done = userData[key]?.completed; const attempts = userData[key]?.attempts || 0; const previousKey = `${classe}_${matiere}_niveau${i}`; const previousDone = i === 0 || userData[previousKey]?.completed; let label = `Niveau ${i + 1}`; let locked = !previousDone; return ( ); }) : null const Header = () => (

HT School Haiti

); const QuestionsNiveau = ({ matiere, niveau, onNextLevel }) => { const [questions, setQuestions] = useState([]); const [currentIndex, setCurrentIndex] = useState(0); const [selectedAnswer, setSelectedAnswer] = useState(null); const [isCorrect, setIsCorrect] = useState(null); const [flashClass, setFlashClass] = useState(""); const goodSound = React.useRef(null); const badSound = React.useRef(null); const shuffleArray = (arr) => arr .map((a) => ({ sort: Math.random(), value: a })) .sort((a, b) => a.sort - b.sort) .map((a) => a.value); useEffect(() => { const fileName = `/capitale/${matiere}_${niveau}.json`; fetch(fileName) .then((res) => res.json()) .then((data) => { const mixed = shuffleArray(data).map(q => ({ ...q, reponse: shuffleArray(q.reponse) })); setQuestions(mixed); }) .catch((err) => console.error(err)); }, [matiere, niveau]); if (questions.length === 0) { return ; } const currentQuestion = questions[currentIndex]; const handleChoice = (choice) => { const correct = choice === currentQuestion.answer; setSelectedAnswer(choice); setIsCorrect(correct); if (correct) { goodSound.current.play(); setFlashClass("flash-green"); } else { badSound.current.play(); setFlashClass("flash-red"); } setTimeout(() => setFlashClass(""), 500); setTimeout(() => { if (currentIndex < questions.length - 1) { setSelectedAnswer(null); setIsCorrect(null); setCurrentIndex((prev) => prev + 1); } }, 2000); }; const isLastQuestion = currentIndex === questions.length - 1 && selectedAnswer; return (
); }; const img ="../image/fond1.png"; const Accueil = ({ onExploreAll, showMiniQuiz }) => (
{showMiniQuiz && { setNiveauActif(2); }} />}
); const IntroductionQuiz = ({ classe, matiere }) => { const textes = { // math: `Bienvenue au quiz de Mathématiques pour la classe de ${classe}. Ce quiz vous aidera à consolider vos acquis en arithmétique, géométrie et algèbre.`, chimie: `Bienvenue au quiz de Chimie – classe de ${classe}. Vous allez tester vos connaissances sur les molécules, réactions chimiques, et plus encore.`, biologie: `Bienvenue au quiz de Biologie pour ${classe}. Préparez-vous à répondre à des questions sur le corps humain, les cellules, et les écosystèmes.`, zoologie: `Bienvenue au quiz de Biologie pour ${classe}. Préparez-vous à répondre à des questions sur les batraciens, les mammifères,les reptiles etc .`, svt: `Bienvenue dans le quiz de svt – ${classe}. préparer vous à répondre à des questions sur la structure cellulaire,la microbiologie, embryologie, la reproduction et la génétique.`, geographie: `Bienvenue au quiz de Géographie pour la classe de ${classe}. Vous allez explorer les continents, les pays, les capitales et la géographie locale.`, }; const texte = textes[matiere] || `Bienvenue dans le quiz de ${matiere} – ${classe}.`; return (

📘 Introduction

{texte}

🧠 Chaque niveau comporte entre 7 et 10 questions. Vous devez obtenir 80% pour débloquer le niveau suivant.

Bonne chance ! 🚀

); }; const MatiereBanner = ({ matiere, onClick }) => (
e.currentTarget.style.transform = "scale(1.05)"} onMouseLeave={e => e.currentTarget.style.transform = "scale(1)"} >

{matiere}

); const ClasseBanner = ({ classe, onClick }) => (

{classe}

); return (

HTSchool

Nos quiz sont adaptés à votre niveau d’études. Chaque niveau terminé vous permet de gagner des points convertibles en gourdes.

{/* === CONTENU PRINCIPAL === */}
{currentTab === "duel" && } {currentTab === "about" && } {currentTab === "contact" && } {!classe && !matiere && !niveauActif && !showDashboard && currentTab === "quiz" && ( <> { setClasse(null); setMatiere(null); setNiveauActif(null); setShowMiniQuiz(false); setshowDashboard(false); setCurrentTab("quiz"); }} showMiniQuiz={showMiniQuiz} /> )} {currentTab === "quiz" && (showDashboard ? ( ) : ( <> {!classe && ( <>

Voudriez vous répondre à des questionnaires de chimie, biologie, anglais, sport etc, alors explorer cette section.

{classes.map((c) => ( handleSelectClasse(c)} /> ))}
)} {classe && !matiere && ( <>

📘 Matières

{(matieres[classe] || []).map((m) => ( handleSelectMatiere(m)} /> ))}
)} {classe && matiere && !niveauActif && ( <>

📚 {matiere} – {classe}

{niveaux.slice(0, 3)}
{niveaux.slice(3)}
{niveaux.length > 3 && ( )}
)} {/* === PUBS RÉCOMPENSÉES === */} {showOnClickA && ( { setShowOnClickA(false); if (pendingNiveau) startNiveau(pendingNiveau, replayAfterAd); setPendingNiveau(null); }} onCancel={() => { setShowOnClickA(false); setPendingNiveau(null); setModal({ title: "Pub annulée", content: "Vous devez regarder la vidéo complète pour débloquer ce niveau.", actions: [ { label: "OK", onClick: () => setModal(null) }, ], }); }} /> )} {showMonetag && ( { setShowMonetag(false); if (pendingNiveau) startNiveau(pendingNiveau, replayAfterAd); setPendingNiveau(null); }} /> )} {/* === QUIZ === */} {questions && niveauActif && ( )} ))} {modal && (

{modal.title}

{modal.content}

{modal.actions.map((a, i) => ( ))}
)}
); }; function UserForm({ onSubmit }) { const [pseudo, setPseudo] = useState(""); const [phone, setPhone] = useState(""); const [isLogin, setIsLogin] = useState(true); const [isyou, setIsyou] = useState(null); const [password, setPassword] = useState(""); const [showForm, setShowForm] = useState(true); useEffect(() => { const savedUser =localStorage.getItem("quizUser"); if (savedUser) { setShowForm(false); onSubmit?.(JSON.parse(savedUser)); // Utilisateur déjà enregistré } }, []); const handleSubmit = (e) => { e.preventDefault(); if (!pseudo || !phone || !password) { alert("Tous les champs sont requis."); return; } localStorage.setItem("quizUser", JSON.stringify({pseudo, phone})); onSubmit?.({pseudo, phone}); if(!isLogin){ User_if({pseudo, phone, password }); } setShowForm(false); }; const handleSubmitcon = (e) => { e.preventDefault(); if (!phone || !password) { alert("Tous les champs sont requis."); return null; } if(isLogin){ Udat_user({phone,password}).then((data)=>{ if(data!=0 && data !=1 ){ localStorage.setItem("quizUser", JSON.stringify({pseudo})); onSubmit?.({phone}); setShowForm(false); }else{ setIsyou("mot passe ou Téléphone incorrect"); setShowForm(true); } }) } }; if (!showForm) return null; if(!isLogin){ return (

📝 Créer un compte joueur

setPseudo(e.target.value)} placeholder="Pseudo" />
setPhone(e.target.value)} />
setPassword(e.target.value)} />

{isLogin ? "Pas encore de compte ?" : "Déjà inscrit ?"}{" "}

); } else if (isLogin){ return (

📝 Connexion

setPhone(e.target.value)} placeholder="Téléphone" focus="focus" />
setPassword(e.target.value)} placeholder="mot de passe"/>

{isLogin ? "Pas encore de compte ?" : "Déjà inscrit ?"}{" "}

{isyou !=null ? isyou : ""}

); } } const Dashboard = ({ userData, classe, matiere,setModal }) => { const [badges, setBadges] = React.useState(0); const [points, setPoints] = React.useState(0); const [user, setUser] = useState(() => { const saved = localStorage.getItem("quizUser"); return saved ? JSON.parse(saved) : null;}); const [loadingWithdrawal, setLoadingWithdrawal] = useState(false); const b = JSON.parse(localStorage.getItem("badges") || "0"); const pts = parseInt(localStorage.getItem("totalPoints") || "0"); React.useEffect(() => { const c = parseInt(b.argent)+parseInt(b.bronze) +parseInt(b.or)+parseInt(b.diamant); if (typeof c === "number" && !isNaN(c)) { setBadges(c); }else{ setBadges(0) } setPoints(pts) }, []); const gourdesbagdes = parseInt(b.diamant)*5 + parseInt(b.or) + parseInt(b.argent)/4 + parseInt(b.bronze)/8+ pts/32; function getGourdes() { if (typeof gourdesbagdes === "number" && !isNaN(gourdesbagdes)) { Updat_bagde(); return Math.round(gourdesbagdes * 100) / 100; }else{return 0} } async function sendWithdrawalRequest(c, setModal) { try { setLoadingWithdrawal(true); const response = await fetch("api/requestWithdrawal.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(c) }); const data = await response.json(); setLoadingWithdrawal(false); if (data.success) { setModal({ title: "✅ Demande envoyée", content: data.message || "Votre demande de retrait a été soumise avec succès.", actions: [{ label: "OK", onClick: () => setModal(null) }] }); } else { setModal({ title: "❌ Échec", content: data.message || "Une erreur est survenue lors de la soumission.", actions: [{ label: "Fermer", onClick: () => setModal(null) }] }); } } catch (error) { console.error("Erreur lors de la requête :", error); setModal({ title: "❌ Erreur réseau", content: "Impossible de contacter le serveur. Veuillez réessayer plus tard.", actions: [{ label: "Fermer", onClick: () => setModal(null) }] }); } } const handleRequestWithdrawal = () => { const username = localStorage.getItem("username"); if (!username || gourdesbagdes < 2500) { setModal({ title: "❗ Retrait impossible", content: "Vous devez avoir au moins 2500 Gdes pour demander un retrait.", actions: [{ label: "OK", onClick: () => setModal(null) }] }); return; }else{ setLoadingWithdrawal(true); const requestData = { username, amount: Math.floor(gourdesbagdes*0.8), phone: localStorage.getItem("phone") || "non renseigné" }; sendWithdrawalRequest(requestData, setModal); } }; const ProgressionParMatiere = () => { return (

📈 Progression par matière ({classe})

{(matieres[classe] || []).map((matiere) => { const total = niveauxParMatiere[matiere] || 0; if (total === 0) return null; let completed = 0; const lastData = JSON.parse(localStorage.getItem("lastNiveauKey")) || {}; // Ajout de la progression finale uniquement si existante dans lastNiveauKey if (classe === "NSIV" && matiere === "chimie" && lastData.chimieNSIV) { completed = parseInt(lastData.chimieNSIV.slice(-1)) || 0; } else if (classe === "9eme" && matiere === "biologie" && lastData.bio9eme) { completed = parseInt(lastData.bio9eme.slice(-1)) || 0; } else { for (let i = 1; i <= total; i++) { const key = `${classe}_${matiere}_niveau${i}`; if (userData[key]?.completed) completed++; } } const pourcentage = total > 0 ? (completed / total) * 100 : 0; return (
{matiere} : {completed}/{total} niveaux complétés
); })}
); }; if (!user) { return ( { localStorage.setItem("quizUser", JSON.stringify(data)); setUser(data); }} /> ); } return (

🎓 Tableau de bord

Total de points : {points}

Badges spéciaux obtenus : {badges}

Mes badges spéciaux

  • 💎
    {b.diamant}
  • 🥇
    {b.or}
  • 🥈
    {b.argent}
  • 🥉
    {b.bronze}

Portefeuille

{getGourdes()} GOURDES {gourdesbagdes>2500 ? ( ):( )}
); }; function PrivacyPolicy() { const [accepted, setAccepted] = useState(false); useEffect(() => { const hasAccepted = localStorage.getItem("privacyAccepted") === "true"; if (hasAccepted) { setAccepted(true); //onAccept?.(); // Sécurité au cas où onAccept est undefined } }, []); const handleAccept = () => { localStorage.setItem("privacyAccepted", "true"); setAccepted(true); onAccept?.(); }; return (

📜 Politique de Confidentialité

Dernière mise à jour : 04/06/2025

Votre confidentialité est importante pour nous. Cette application collecte :

  • Nom d’utilisateur (pseudo)
  • Numéro de téléphone
  • Progression dans les quiz
  • Récompenses, badges et scores
  • Historique

Les données sont utilisées uniquement pour suivre vos progrès et attribuer vos récompenses. Aucune donnée n’est vendue ou partagée à des tiers.

En jouant à notre jeux, automatiquement, vous acceptez cette politique.

Nous nous reservons le droit de modifier cette politique.

); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render(React.createElement(App)); const cookieRoot = ReactDOM.createRoot(document.getElementById("cookie-root")); cookieRoot.render(React.createElement(CookieBanner)); function DuelApp() { const [config, setConfig] = React.useState({}); const [matiere, setMatiere] = React.useState(""); const [questions, setQuestions] = React.useState([]); const [currentIndex, setCurrentIndex] = React.useState(0); const [player, setPlayer] = React.useState(""); const [roomId, setRoomId] = React.useState(""); const [score, setScore] = React.useState(0); const [quizStarted, setQuizStarted] = React.useState(false); const [timer, setTimer] = React.useState(10); const [feedback, setFeedback] = React.useState(""); // "correct" | "wrong" const [ended, setEnded] = React.useState(false); const [finalScores, setFinalScores] = React.useState({}); const current = questions[currentIndex]; React.useEffect(() => { fetch("config.json") .then((res) => res.json()) .then((data) => setConfig(data)); }, []); const createRoom = () => { const id = Math.random().toString(36).substr(2, 6).toUpperCase(); setRoomId(id); setPlayer("player1"); alert(`Code à partager : ${id}`); }; const joinRoom = () => { const code = prompt("Code room :"); if (code) { firebase.database().ref(`rooms/${code}`).once("value", (snap) => { if (snap.exists()) { setRoomId(code); setPlayer("player2"); } else { alert("Room inexistante"); } }); } }; const handleMatiereChange = (e) => { const mat = e.target.value; setMatiere(mat); const total = config[mat]; if (!total) return alert("Pas de niveau dispo."); const niv = Math.floor(Math.random() * total) + 1; const fichier = `capitale/${mat}_${niv}.json`; fetch(fichier) .then((res) => res.json()) .then((data) => { setQuestions(data); setQuizStarted(true); const ref = firebase.database().ref(`rooms/${roomId}`); if (player === "player1") { ref.set({ matiere: mat, niveau: niv, questions: data, scores: { player1: 0, player2: 0 }, index: 0, }); } }); }; // React.useEffect(() => { // if (!roomId) return; // const ref = firebase.database().ref(`rooms/${roomId}`); // ref.on("value", (snap) => { // const data = snap.val(); // if (data) { // if (data.questions) setQuestions(data.questions); // if (data.index !== undefined) setCurrentIndex(data.index); // if (data.scores && player) { // setScore(data.scores[player]); // setFinalScores(data.scores); // } // } // }); // return () => ref.off(); // }, [roomId, player]); React.useEffect(() => { if (!roomId) return; const ref = firebase.database().ref(`rooms/${roomId}`); ref.on("value", (snap) => { const data = snap.val(); if (data) { if (data.questions && data.questions.length) { setQuestions(data.questions); setQuizStarted(true); // 👈 Ajout essentiel pour joueur 2 } if (data.index !== undefined) setCurrentIndex(data.index); if (data.scores && player) { setScore(data.scores[player]); setFinalScores(data.scores); } } }); return () => ref.off(); }, [roomId, player]); // Chrono par question React.useEffect(() => { if (!quizStarted || ended) return; setTimer(10); const interval = setInterval(() => { setTimer((t) => { if (t <= 1) { clearInterval(interval); handleAnswer(null); } return t - 1; }); }, 1000); return () => clearInterval(interval); }, [currentIndex, quizStarted]); const handleAnswer = (rep) => { if (!current || ended) return; const isCorrect = rep && current.answer.includes(rep); setFeedback(isCorrect ? "correct" : "wrong"); const ref = firebase.database().ref(`rooms/${roomId}`); if (isCorrect) { ref.child("scores").child(player).transaction((val) => (val || 0) + 1); } setTimeout(() => { setFeedback(""); if (player === "player1" && currentIndex + 1 < questions.length) { ref.child("index").set(currentIndex + 1); } if (currentIndex + 1 >= questions.length) { setEnded(true); setQuizStarted(false); setTimeout(() => { firebase.database().ref(`rooms/${roomId}`).remove(); }, 4000); } }, 1000); }; const renderEnd = () => { if (!ended) return null; const s1 = finalScores.player1 || 0; const s2 = finalScores.player2 || 0; let result; if (s1 === s2) result = "Égalité !"; else if ((player === "player1" && s1 > s2) || (player === "player2" && s2 > s1)) { result = "🎉 Tu as gagné !"; } else { result = "😢 Tu as perdu."; } return (

Résultat final

Player 1 : {s1} | Player 2 : {s2}

{result}

); }; if (!roomId) { return (

Duel Multijoueur

); } if (!quizStarted && player === "player1" && !ended) { return (

Choisis une matière

); } if (!quizStarted && !ended) { return

En attente de questions…

; } const getAdversaire = () => (player === "player1" ? "player2" : "player1"); return (

{player === "player1" ? "🧑 Joueur 1" : "🧑 Joueur 2"} | Room: {roomId}

Question {currentIndex + 1} / {questions.length}

Temps restant : {timer}s

{current?.question}

{current?.reponse.map((r, i) => ( ))}
🧑 Ton score : {score}
👤 Score adversaire : {finalScores[getAdversaire()] || 0}
{renderEnd()}
); }