191 lines
6.2 KiB
HTML
191 lines
6.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="theme-color" content="#667eea">
|
|
<title>Boutique — Bons Points</title>
|
|
<link rel="manifest" href="/manifest.json">
|
|
<link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192.png">
|
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
<meta name="apple-mobile-web-app-title" content="Bons Points">
|
|
<meta name="mobile-web-app-capable" content="yes">
|
|
<link rel="stylesheet" href="/css/style.css?v=34">
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<a id="lien-retour" class="btn-retour">
|
|
<svg class="btn-retour-icone" width="18" height="18" viewBox="0 0 24 24"
|
|
fill="none" stroke="currentColor" stroke-width="2.5"
|
|
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<polyline points="15 18 9 12 15 6"/>
|
|
</svg>
|
|
Retour
|
|
</a>
|
|
|
|
<div id="boutique-contenu" class="cacher">
|
|
<div class="fiche-entete">
|
|
<div id="avatar-boutique" class="avatar-cercle fiche-avatar-centre"></div>
|
|
<div id="score-bandeau" class="score-bandeau"></div>
|
|
</div>
|
|
|
|
<p class="etape-label">Choisis ta récompense</p>
|
|
<div id="liste-recompenses" class="grille-recompenses"></div>
|
|
</div>
|
|
|
|
<p id="erreur" class="erreur-page cacher">Enfant introuvable</p>
|
|
</div>
|
|
|
|
<div id="modal-confirm" class="modal">
|
|
<div class="modal-contenu">
|
|
<p id="modal-texte"></p>
|
|
<div class="modal-boutons">
|
|
<button class="btn btn-principal" id="btn-confirmer">Oui !</button>
|
|
<button class="btn btn-secondaire" id="btn-annuler">Non</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="feedback" class="feedback"></div>
|
|
|
|
<script>
|
|
const prenomUrl = window.location.pathname.split('/').pop();
|
|
const PHOTOS = {
|
|
Ariana: '/photos/ariana.png',
|
|
Pablo: '/photos/pablo.png',
|
|
'Hélia': '/photos/helia.png',
|
|
};
|
|
|
|
function slug(prenom) {
|
|
return prenom.toLowerCase()
|
|
.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
}
|
|
|
|
let enfant = null;
|
|
let recompenses = [];
|
|
let recompenseChoisie = null;
|
|
|
|
const feedback = document.getElementById('feedback');
|
|
const modal = document.getElementById('modal-confirm');
|
|
|
|
function afficherFeedback(msg) {
|
|
feedback.textContent = msg;
|
|
feedback.classList.add('visible');
|
|
setTimeout(() => feedback.classList.remove('visible'), 2500);
|
|
}
|
|
|
|
function mettreAJourBandeau() {
|
|
const bandeau = document.getElementById('score-bandeau');
|
|
bandeau.innerHTML = `
|
|
<span class="score-bandeau-nom">${enfant.prenom}</span>
|
|
<span class="score-bandeau-pts" style="color:${enfant.couleur}">
|
|
${enfant.score} points
|
|
</span>
|
|
`;
|
|
bandeau.style.borderColor = enfant.couleur;
|
|
}
|
|
|
|
function afficherRecompenses() {
|
|
mettreAJourBandeau();
|
|
|
|
const cont = document.getElementById('liste-recompenses');
|
|
cont.innerHTML = recompenses.map((r) => {
|
|
const ok = enfant.score >= r.cout_points;
|
|
return `
|
|
<button class="carte-recompense ${ok ? '' : 'indisponible'}"
|
|
data-id="${r.id}" ${ok ? '' : 'disabled'}>
|
|
<span class="rec-icone">${r.icone}</span>
|
|
<span class="rec-libelle">${r.libelle}</span>
|
|
<span class="rec-cout">${r.cout_points} pts</span>
|
|
</button>
|
|
`;
|
|
}).join('');
|
|
|
|
cont.querySelectorAll('.carte-recompense:not(.indisponible)').forEach((btn) => {
|
|
btn.addEventListener('click', () => {
|
|
recompenseChoisie = recompenses.find(
|
|
(x) => x.id === parseInt(btn.dataset.id, 10),
|
|
);
|
|
document.getElementById('modal-texte').textContent =
|
|
`${enfant.prenom}, tu veux « ${recompenseChoisie.libelle} » ` +
|
|
`pour ${recompenseChoisie.cout_points} points ?`;
|
|
modal.classList.add('ouvert');
|
|
});
|
|
});
|
|
}
|
|
|
|
function fermerModal() {
|
|
modal.classList.remove('ouvert');
|
|
}
|
|
|
|
async function confirmerAchat() {
|
|
fermerModal();
|
|
const res = await fetch('/api/public/acheter', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
enfant_id: enfant.id,
|
|
recompense_id: recompenseChoisie.id,
|
|
}),
|
|
});
|
|
|
|
const data = await res.json();
|
|
|
|
if (!res.ok) {
|
|
afficherFeedback(data.erreur || 'Erreur');
|
|
return;
|
|
}
|
|
|
|
enfant = data.enfant;
|
|
|
|
afficherFeedback(
|
|
`Bravo ${data.enfant.prenom} ! ${data.recompense.icone} ` +
|
|
`${data.recompense.libelle}`,
|
|
);
|
|
afficherRecompenses();
|
|
}
|
|
|
|
async function charger() {
|
|
try {
|
|
const [resEnfant, resRecompenses] = await Promise.all([
|
|
fetch(`/api/public/enfant/${prenomUrl}`),
|
|
fetch('/api/public/recompenses'),
|
|
]);
|
|
|
|
if (!resEnfant.ok) throw new Error('introuvable');
|
|
|
|
const data = await resEnfant.json();
|
|
enfant = data.enfant;
|
|
recompenses = (await resRecompenses.json()).recompenses;
|
|
|
|
const avatar = document.getElementById('avatar-boutique');
|
|
avatar.style.borderColor = enfant.couleur;
|
|
avatar.innerHTML =
|
|
`<img src="${PHOTOS[enfant.prenom]}" alt="${enfant.prenom}">`;
|
|
|
|
mettreAJourBandeau();
|
|
document.getElementById('lien-retour').href =
|
|
`/enfant/${slug(enfant.prenom)}`;
|
|
document.title = `Boutique — ${enfant.prenom}`;
|
|
|
|
afficherRecompenses();
|
|
document.getElementById('boutique-contenu').classList.remove('cacher');
|
|
} catch {
|
|
document.getElementById('erreur').classList.remove('cacher');
|
|
}
|
|
}
|
|
|
|
document.getElementById('btn-confirmer').addEventListener('click', confirmerAchat);
|
|
document.getElementById('btn-annuler').addEventListener('click', fermerModal);
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal) fermerModal();
|
|
});
|
|
|
|
charger();
|
|
</script>
|
|
<script src="/js/pwa.js" defer></script>
|
|
</body>
|
|
</html>
|