https://fonts.googleapis.com https://fonts.gstatic.com https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap
Donation

💕 Saint-Valentin 💕

Trouve ta moitié !

Rentre tes coordonnées avec ton numéro de cœur. Quand ton duo aura rentré les siennes, tu verras ses infos ! Et faites tourner le site entre vous plus il y a de gens connectés plus il y a de chances de trouver sa moitié.
✅ Tes coordonnées ont été enregistrées ! Ton duo t'appellera quand il se sera connecté.
function setOrderTotalQuantityField(){ const totalQty = panier.reduce((acc,p)=>acc + p.quantite, 0); const qtyField = document.getElementById('quantite'); if(qtyField){ qtyField.value = Math.max(1, totalQty || 1); // empêche la valeur “1” forcée si panier vide lors de tests } } function updatePanierUI(){ const total = panier.reduce((acc,p)=>acc+p.quantite,0); nbPanierEl.textContent = total; nbPanierLarge.textContent = total; // maj champ quantité totale setOrderTotalQuantityField(); panierTable.innerHTML = ''; let montant = 0; panier.forEach(item=>{ const prod = produits[item.id]; const prixTotal = prod.prix * item.quantite; montant += prixTotal; const row = document.createElement('tr'); row.innerHTML = ` ${prod.titre} €${prixTotal.toFixed(2)} `; panierTable.appendChild(row); }); totalPanierEl.textContent = `€${montant.toFixed(2)}`; document.querySelectorAll('.qtyInput').forEach(input=>{ input.addEventListener('change',e=>{ const id = e.target.dataset.id; const val = Math.max(1, Math.min(parseInt(e.target.value) || 1, produits[id].stock)); e.target.value = val; const p = panier.find(x=>x.id===id); if(p) p.quantite = val; updatePanierUI(); }); }); document.querySelectorAll('.removeBtn').forEach(btn=>{ btn.addEventListener('click',e=>{ panier = panier.filter(x=>x.id!==e.target.dataset.id); updatePanierUI(); }); }); // Désactiver "Commander le panier" si non connecté ou panier vide const commanderBtn = document.getElementById('commanderPanierBtn'); commanderBtn.disabled = !logged || panier.length===0; } document.getElementById('viderPanierBtn').addEventListener('click',()=>{ if(confirm('Vider le panier ?')){ panier=[]; updatePanierUI(); } }); document.getElementById('commanderPanierBtn').addEventListener('click',()=>{ if(!logged){ alert('Vous devez être connecté pour commander !'); ouvrirAuth('login'); return; } if(panier.length===0){ alert('Panier vide !'); return; } setOrderTotalQuantityField(); // force la bonne valeur juste avant l’affichage showSection(sections.form); }); document.getElementById('btnOrderSingle').addEventListener('click',()=>{ if(!logged){ alert('Vous devez être connecté pour commander !'); ouvrirAuth('login'); return; } if(!currentDetailId) return; if(!panier.find(p=>p.id===currentDetailId)){ panier.push({id:currentDetailId, quantite: parseInt(document.getElementById('detailQuantiteInput').value)||1}); updatePanierUI(); } setOrderTotalQuantityField(); showSection(sections.form); }); document.getElementById('btnCancelOrder').addEventListener('click',()=>showSection(sections.accueil)); /* =========================== COMMANDE → WEB3FORMS =========================== */ orderForm.addEventListener('submit', async (e)=>{ e.preventDefault(); // champs requis const prenom = document.getElementById('prenom').value.trim(); const nom = document.getElementById('nom').value.trim(); const email = document.getElementById('emailField').value.trim(); const codepostal = document.getElementById('codepostal').value.trim(); if(!prenom || !nom || !email){ alert('Veuillez remplir au minimum prénom, nom et email.'); return; } if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)){ alert('Adresse e-mail invalide.'); return; } if(!/^[0-9]{5}$/.test(codepostal)){ alert('Code postal invalide (5 chiffres).'); return; } // Récap panier const items = panier.map(item=>{ const prod = produits[item.id]; return { id: item.id, titre: prod.titre, quantite: item.quantite, prix_unitaire: prod.prix, prix_total: +(prod.prix * item.quantite).toFixed(2) }; }); const totalPanier = items.reduce((acc, it)=>acc + it.prix_total, 0).toFixed(2); // Met à jour la quantité totale (fix principal) const totalQty = panier.reduce((acc,p)=>acc + p.quantite, 0); document.getElementById('quantite').value = Math.max(1, totalQty || 1); const data = new FormData(orderForm); data.append('subject', `Nouvelle commande — ${prenom} ${nom}`); data.append('from_name', `${prenom} ${nom}`); data.append('cart_total_eur', `€${totalPanier}`); data.append('cart_items_json', JSON.stringify(items)); data.append('logged_user', currentUserName || 'Invité'); data.append('site', 'Objets 3D — Axel & Noah'); btnSubmit.disabled = true; formStatus.textContent = '⏳ Envoi en cours...'; try { const res = await fetch('https://api.web3forms.com/submit', { method:'POST', body:data }); const result = await res.json(); if(result.success){ formStatus.textContent = '✅ Commande envoyée ! Merci 🙏'; alert('Commande envoyée ! Un e-mail de confirmation vous sera envoyé.'); panier = []; updatePanierUI(); orderForm.reset(); showSection(sections.accueil); }else{ console.error('Web3Forms error:', result); formStatus.textContent = '❌ Erreur lors de l’envoi. Réessaie plus tard.'; alert('Une erreur est survenue : ' + (result.message || 'Envoi impossible')); } } catch(err){ console.error('Network error:', err); formStatus.textContent = '❌ Erreur réseau. Vérifie ta connexion.'; alert('Erreur réseau pendant l’envoi. Réessaie.'); } finally { btnSubmit.disabled = false; setTimeout(()=>{ formStatus.textContent=''; }, 3500); } }); /* ==================== AUTH / EMAIL HELPERS ==================== */ function ouvrirAuth(type){ document.getElementById('authModalOverlay').classList.add('active'); changerForm(type); } function fermerAuth(){ document.getElementById('authModalOverlay').classList.remove('active'); } function changerForm(type){ const loginForm = document.getElementById('loginForm'); const registerForm = document.getElementById('registerForm'); const title = document.getElementById('authTitle'); if(type === 'login'){ loginForm.style.display = 'block'; registerForm.style.display = 'none'; title.textContent = 'Connexion'; } else { loginForm.style.display = 'none'; registerForm.style.display = 'block'; title.textContent = 'Inscription'; } } // stockage utilisateurs (local) function loadUsers(){ try{ const raw = localStorage.getItem('users'); return raw ? JSON.parse(raw) : {}; } catch(e){ return {}; } } function saveUsers(users){ localStorage.setItem('users', JSON.stringify(users)); } let users = loadUsers(); async function hashPassword(pass){ const enc = new TextEncoder().encode(pass); const buf = await crypto.subtle.digest('SHA-256', enc); const arr = Array.from(new Uint8Array(buf)); return arr.map(b=>b.toString(16).padStart(2,'0')).join(''); } function isStrongPassword(pwd){ return typeof pwd==='string' && pwd.length>=8 && /[A-Za-z]/.test(pwd) && /[0-9]/.test(pwd); } function isValidEmailFormat(email){ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); } const bannedWords = ['example','test','no-reply','noreply']; function containsBannedWords(email){ const lower=email.toLowerCase(); return bannedWords.some(w=>lower.includes(w)); } document.getElementById('openLoginBtn').addEventListener('click',()=>ouvrirAuth('login')); document.getElementById('openRegisterBtn').addEventListener('click',()=>ouvrirAuth('register')); document.getElementById('logoutBtn').addEventListener('click',()=>{ logged=false; currentUserName=''; updateUserUI(); updatePanierUI(); }); document.getElementById('loginBtn').addEventListener('click', async ()=>{ const email = document.getElementById('loginEmail').value.trim(); const password = document.getElementById('loginPassword').value; if(!email || !password){ alert('Veuillez remplir tous les champs'); return; } if(!isValidEmailFormat(email)){ alert('Format de l’e-mail invalide.'); return; } const key = email.toLowerCase(); const user = users[key]; if(!user){ alert('Adresse non reconnue. Inscrivez-vous d’abord.'); return; } const passHash = await hashPassword(password); if(user.passHash !== passHash){ alert('Mot de passe incorrect.'); return; } // 🔁 MODIFICATION DEMANDÉE : prénom uniquement (ou email en secours) currentUserName = user.prenom ? user.prenom : email; logged = true; updateUserUI(); fermerAuth(); document.getElementById('loginEmail').value = ''; document.getElementById('loginPassword').value = ''; updatePanierUI(); }); document.getElementById('registerBtn').addEventListener('click', async ()=>{ const prenom = document.getElementById('regPrenom').value.trim(); const nom = document.getElementById('regNom').value.trim(); const adresse = document.getElementById('regAdresse').value.trim(); const codepostal = document.getElementById('regCodepostal').value.trim(); const email = document.getElementById('regEmail').value.trim(); const emailConfirm = document.getElementById('regEmailConfirm').value.trim(); const password = document.getElementById('regPassword').value; if(!prenom || !nom || !adresse || !codepostal || !email || !emailConfirm || !password){ alert('Veuillez remplir tous les champs'); return; } if(email !== emailConfirm){ alert('Les deux adresses e-mail ne correspondent pas.'); return; } if(!isValidEmailFormat(email)){ alert('Format de l’e-mail invalide.'); return; } if(!/^[0-9]{5}$/.test(codepostal)){ alert('Code postal invalide (5 chiffres).'); return; } if(containsBannedWords(email)){ alert('Adresse e-mail refusée.'); return; } if(!isStrongPassword(password)){ alert('Mot de passe trop faible. Min. 8 caractères avec lettres et chiffres.'); return; } const key = email.toLowerCase(); if(users[key]){ alert('Cette adresse e-mail est déjà utilisée.'); return; } const passHash = await hashPassword(password); users[key] = { prenom, nom, adresse, codepostal, passHash }; saveUsers(users); // 🔔 Notification d’inscription via Web3Forms (Option A) try{ const fd = new FormData(); fd.append('access_key','867b9f7b-a375-47c5-a0c0-ec184b69f529'); fd.append('email_to','objet.3d.axel.et.noah@gmail.com'); fd.append('subject','Nouvelle inscription'); fd.append('from_name', `${prenom} ${nom}`); fd.append('message', `Un nouvel utilisateur s'est inscrit.\nNom: ${prenom} ${nom}\nEmail: ${email}\nCode postal: ${codepostal}`); await fetch('https://api.web3forms.com/submit', { method:'POST', body:fd }); }catch(err){ console.warn('Notification inscription non envoyée (réseau) :', err); } alert(`✅ Inscription réussie ! Bienvenue ${prenom} ${nom}`); // 🔁 MODIFICATION DEMANDÉE : prénom uniquement currentUserName = prenom; logged = true; updateUserUI(); fermerAuth(); // Reset document.getElementById('regPrenom').value = ''; document.getElementById('regNom').value = ''; document.getElementById('regAdresse').value = ''; document.getElementById('regCodepostal').value = ''; document.getElementById('regEmail').value = ''; document.getElementById('regEmailConfirm').value = ''; document.getElementById('regPassword').value = ''; updatePanierUI(); }); function updateUserUI(){ const notLogged = document.getElementById('notLogged'); const loggedDiv = document.getElementById('logged'); const userStatus = document.getElementById('userStatus'); const badgeUser = document.getElementById('badgeUser'); if(logged){ notLogged.style.display = 'none'; loggedDiv.style.display = 'flex'; badgeUser.textContent = currentUserName; userStatus.textContent = currentUserName; } else { notLogged.style.display = 'flex'; loggedDiv.style.display = 'none'; userStatus.textContent = 'Invité'; } } /* ==================== BACK BUTTON ==================== */ const backBtn = document.getElementById('backBtn'); let sectionHistory = []; const originalShowSection = showSection; showSection = function(sec){ if(sectionHistory[sectionHistory.length - 1] !== sec){ sectionHistory.push(sec); } Object.values(sections).forEach(s => s.style.display = 'none'); sec.style.display = 'block'; backBtn.style.display = sectionHistory.length > 1 ? 'block' : 'none'; }; backBtn.addEventListener('click', () => { if(sectionHistory.length > 1){ sectionHistory.pop(); const prev = sectionHistory[sectionHistory.length - 1]; Object.values(sections).forEach(s => s.style.display = 'none'); prev.style.display = 'block'; } backBtn.style.display = sectionHistory.length > 1 ? 'block' : 'none'; }); /* ==================== INIT ==================== */ updateUserUI(); showSection(sections.accueil);