Aller au contenu principal

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à :

ColonneTypeDescription
scorebigintLe nombre de bonnes réponses
tempsbigintLe temps total mis en secondes
date_partiedateLa 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 JavaScriptUtilité
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


Pourquoi enregistrer le score dans la base de données ?


Quelle méthode permet de mesurer le temps écoulé ?


Quel type de données est utilisé pour le score ?


À quel moment faut-il enregistrer le score ?


Que fait `.split('T')[0]` sur une date ISO ?



TP pour réfléchir et résoudre des problèmes

Objectif

Modifier votre application Quiz Cyber pour :

  1. Enregistrer le score actuel dans la base classement après chaque réponse
  2. Enregistrer également le temps écoulé
  3. 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

Rappel

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, classement et classement_joueur soient sécurisées

4.1. Activer les RLS sur les 3 tables

Dans Supabase Studio :

  1. Allez dans Table Editor
  2. Sélectionnez chaque table (joueur, classement, classement_joueur)
  3. 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

TableActionAutorisé si...
joueurSELECTauth.uid() = id
classementINSERT✅ (pas lié à l’utilisateur direct)
classement_joueurSELECTauth.uid() = id_joueur
classement_joueurINSERTauth.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