Enregistrer le score
Enregistrer le score du joueur dans Supabase au fur et à mesure du quiz (après chaque réponse), afin de pouvoir afficher un classement plus tard.
Notions théoriques
Pourquoi faut-il enregistrer le score dans la base de données ?
Pour que votre application soit interactive et utile à plusieurs joueurs, il faut que les données soient stockées durablement.
Enregistrer le score permet de :
- Conserver les performances d’un joueur
- Afficher un classement à la fin du quiz
- Comparer les résultats entre les joueurs
- Motiver les joueurs à progresser
Quand faut-il enregistrer le score ?
Pas seulement à la fin du quiz !
Nous allons enregistrer le score après chaque réponse, pour ne rien perdre si la page est fermée ou si le joueur abandonne.
Où enregistrer les scores ?
Dans la table classement, vous avez déjà :
| Colonne | Type | Description |
|---|---|---|
score | bigint | Le nombre de bonnes réponses |
temps | bigint | Le temps total mis en secondes |
date_partie | date | La date du quiz |
Et dans la table classement_joueur, vous faites le lien entre un joueur et un score.
Comment mesurer le temps ?
Nous allons enregistrer l’heure de début du quiz avec :
const debut = Date.now();
Puis, à la fin du quiz :
const tempsTotal = Math.floor((Date.now() - debut) / 1000); // en secondes
Comment insérer ou mettre à jour un score dans Supabase ?
Supabase vous permet d’utiliser :
await supabase.from("classement").insert({ score, temps, date_partie });
Mais attention : il faut lier ce score à un joueur !
C’est pourquoi on insère aussi dans la table classement_joueur :
await supabase.from("classement_joueur").insert({
id_joueur: joueurId,
id_classement: idClassementInsere,
});
Exemple pratique
Voici un exemple simplifié d’enregistrement du score après une réponse :
const { data: insertClassement } = await supabase
.from("classement")
.insert({
score: scoreActuel,
temps: Math.floor((Date.now() - debut) / 1000),
date_partie: new Date().toISOString().split('T')[0],
})
.select();
const idClassement = insertClassement?.[0]?.id;
await supabase.from("classement_joueur").insert({
id_joueur: joueurId,
id_classement: idClassement,
});
Quelques méthodes à connaître
| Méthode JavaScript | Utilité |
|---|---|
Date.now() | Renvoie l'heure actuelle en millisecondes |
Math.floor() | Arrondit à l'entier inférieur |
new Date().toISOString() | Donne la date actuelle au format ISO |
.split('T')[0] | Permet de ne garder que la date (sans l’heure) |
await supabase.from(...).insert(...).select() | Insère une ligne et récupère son ID |
Test de mémorisation / compréhension
TP pour réfléchir et résoudre des problèmes
Objectif
Modifier votre application Quiz Cyber pour :
- Enregistrer le score actuel dans la base
classementaprès chaque réponse - Enregistrer également le temps écoulé
- Lier le classement au joueur dans la table
classement_joueur
Étapes
Étape 1 — Créer un état debut
Dans app/page.tsx, ajoutez un état pour enregistrer le début du quiz :
const [debut, setDebut] = useState<number | null>(null);
Et dans le useEffect qui charge les questions :
useEffect(() => {
fetchQuestion();
setDebut(Date.now());
}, []);
Étape 2 — Ajouter la fonction enregistrerScore
async function enregistrerScore(score: number) {
const joueurId = localStorage.getItem("joueur_id");
if (!joueurId || debut === null) return;
const temps = Math.floor((Date.now() - debut) / 1000);
const { data: classementData, error: classementError } = await supabase
.from("classement")
.insert({
score,
temps,
date_partie: new Date().toISOString().split("T")[0],
})
.select();
if (classementError) {
console.error("Erreur enregistrement score :", classementError);
return;
}
const idClassement = classementData?.[0]?.id;
if (idClassement) {
await supabase.from("classement_joueur").insert({
id_joueur: joueurId,
id_classement: idClassement,
});
}
}
Étape 3 — Appeler cette fonction après chaque bonne réponse
Dans la fonction handleClick, ajoutez :
if (reponse.est_correcte) {
setScore((prev) => {
const nouveauScore = prev + 1;
enregistrerScore(nouveauScore);
return nouveauScore;
});
}
Étape 4 — Créer des RLS
Pour garantir que chaque joueur n’accède qu’à ses propres scores, les Row-Level Security (RLS) sont indispensables dans Supabase.
Il faut configurer les RLS pour que :
- Un joueur ne puisse lire que ses propres scores
- Un joueur ne puisse écrire que ses propres scores
- Les relations entre les tables
joueur,classementetclassement_joueursoient sécurisées
4.1. Activer les RLS sur les 3 tables
Dans Supabase Studio :
- Allez dans Table Editor
- Sélectionnez chaque table (
joueur,classement,classement_joueur) - Activez Row Level Security
4.2. Politique pour la table joueur
Lecture / Écriture uniquement pour soi-même
create policy "Un joueur peut voir/modifier ses propres données"
on public.joueur
for all
using (auth.uid() = id);
4.3. Politique pour la table classement
Un joueur peut insérer un score, mais pas lire tous les scores
create policy "Un joueur peut insérer un score"
on public.classement
for insert
with check (true); -- On autorise tous les inserts (aucune relation directe avec l'utilisateur ici)
On n’autorise pas la lecture ici, car les scores sont liés via
classement_joueur.
4.4. Politique pour la table classement_joueur
Un joueur peut :
- lire ses propres scores
- écrire uniquement pour lui-même
create policy "Un joueur peut lire ses propres scores"
on public.classement_joueur
for select
using (
auth.uid() = id_joueur
);
create policy "Un joueur peut insérer un lien vers un score"
on public.classement_joueur
for insert
with check (
auth.uid() = id_joueur
);
Résumé des politiques RLS
| Table | Action | Autorisé si... |
|---|---|---|
joueur | SELECT | auth.uid() = id |
classement | INSERT | ✅ (pas lié à l’utilisateur direct) |
classement_joueur | SELECT | auth.uid() = id_joueur |
classement_joueur | INSERT | auth.uid() = id_joueur |
Résultat attendu
À chaque bonne réponse :
- Le score est augmenté
- Le score est enregistré dans la base
- Le temps écoulé est calculé et inséré
- Le joueur est lié à son score
Corrigé du TP
Une solution
Vous devez être connecté pour voir le contenu.