Skip to main content

Vue d’ensemble

Le système de quiz permet aux associations d’évaluer leur maturité numérique via un questionnaire interactif de 7 questions. Le moteur V2 utilise un scoring pondéré pour recommander les outils les plus pertinents, déterminer un profil utilisateur (Débutant, Transition, Connecté) et générer un plan d’action personnalisé. Caractéristiques principales :
  • Moteur de scoring V2 avec pondération par réponse
  • 3 profils : Débutant, Transition, Connecté
  • Top 6 outils recommandés avec scores et raisons
  • Plan d’action en 3 étapes
  • Branchement conditionnel entre questions
  • Résultats partageables via URL encodée
  • Persistence de progression (localStorage, 24h TTL)
  • Capture d’email + envoi de rapport par email (Netlify Function + Resend)
  • Interface publique + gestion admin complète

Architecture V2 — Scoring pondéré

Principe

Chaque réponse du quiz porte des scoring_weights (JSONB) qui attribuent des points à des outils, catégories ou tiers de pricing. En fin de quiz, les points sont agrégés par outil pour produire un classement.
3 types de clés supportés :
  • tool:<id> — Points directs pour un outil spécifique
  • category:<name> — Points pour tous les outils d’une catégorie
  • pricing:<tier> — Points pour tous les outils d’un tier de pricing

Flux de scoring


Tables impliquées

Le système quiz utilise 5 tables PostgreSQL gérées via Hasura GraphQL : 1. quizzes — Définitions des quiz
2. quiz_questions — Questions du quiz
3. quiz_answers — Options de réponse avec scoring
4. quiz_recommendations — Règles de recommandation (legacy V1)
La table quiz_recommendations et son condition_logic sont le système V1 (règles conditionnelles). Le moteur V2 utilise les scoring_weights sur quiz_answers à la place. Les deux systèmes coexistent dans le code.
5. quiz_responses — Soumissions utilisateurs

Moteur de scoring V2

calculateScores()

Fichier : src/api/quiz/scoring.ts
Algorithme :
  1. Initialise un score de 0 pour chaque outil du catalogue
  2. Pour chaque réponse de l’utilisateur, trouve les scoring_weights correspondants
  3. Pour chaque clé de poids :
    • tool:<id> → ajoute les points directement à l’outil
    • category:<name> → ajoute les points à tous les outils de la catégorie
    • pricing:<tier> → ajoute les points à tous les outils du tier
  4. Accumule les “raisons” (texte de la question + réponse) pour expliquer le score
  5. Filtre les outils avec score > 0, trie par score décroissant
  6. Retourne les maxTools meilleurs résultats

determineProfile()

Classifie l’utilisateur en 3 profils basés sur ses réponses :

generateSummary()

Génère un résumé personnalisé basé sur les réponses (taille d’équipe, budget, priorités).

Types de retour


Branchement conditionnel

Principe

Les questions peuvent rediriger vers d’autres questions selon la réponse choisie, via le champ next_question_rules :
Si aucune règle ne correspond ou si next_question_rules est null, le quiz avance séquentiellement (par order_index). Fichier : src/features/quiz/hooks/useQuiz.ts
Retour arrière : Le hook maintient un questionHistory (pile d’indices) pour naviguer correctement en arrière même avec le branchement.

Résultats partageables

Les résultats du quiz sont encodés en base64 dans l’URL pour permettre le partage :
Quand un lien partagé est ouvert, le quiz charge directement les résultats sans repasser par les questions.

Persistence de progression

La progression du quiz est sauvegardée dans localStorage pour permettre la reprise :
  • Sauvegarde automatique à chaque réponse et changement d’étape
  • Restauration au rechargement de la page (même slug, < 24h)
  • Nettoyage automatique après complétion du quiz ou expiration

Composants UI

Architecture des composants

QuizContainer — Orchestrateur

Gère le flux : Start -> Questions -> Results

QuizResults — Affichage des résultats

Affiche le rapport complet :
  • Badge du profil (Débutant / Transition / Connecté)
  • Résumé personnalisé
  • Cartes des outils recommandés (logo, pricing, catégorie, raisons)
  • Plan d’action en 3 étapes
  • Formulaire de capture d’email
  • Bouton de partage (copie l’URL)
  • Bouton de réinitialisation

Hook useQuiz

Fichier : src/features/quiz/hooks/useQuiz.ts (440 lignes) Hook principal qui orchestre toute la logique du quiz :
Flux de completeQuiz() :
  1. Appelle quizApi.getRecommendationsByScoring() avec les réponses
  2. Stocke le QuizReport dans le state
  3. Met à jour l’URL avec le lien partageable
  4. Soumet la réponse pour analytics via quizApi.submitResponse()

Envoi de rapport par email

Netlify Function

Fichier : netlify/functions/send-report.ts Une Netlify Function envoie le rapport par email via l’API Resend :
  • Endpoint : POST /.netlify/functions/send-report
  • Rate limiting : 3 emails par IP par heure
  • Validation : Email requis et valide, outils requis
  • CORS : Restreint à https://kit.lucdidion.lu
  • Template : Email HTML avec profil, résumé, outils recommandés, plan d’action, lien vers résultats
La variable d’environnement RESEND_API_KEY doit être configurée dans les settings Netlify pour que l’envoi d’email fonctionne.

API Layer (GraphQL)

Fichier : src/api/quiz/index.ts
Query GraphQL pour charger un quiz :

Permissions Hasura

  • public : SELECT sur quizzes (actifs), quiz_questions, quiz_answers
  • public : INSERT sur quiz_responses (soumission des réponses)
  • admin : CRUD complet sur toutes les tables quiz

Bonnes pratiques

A faire

  • Toujours utiliser getRecommendationsByScoring() (V2) pour les nouveaux quiz
  • Configurer les scoring_weights sur chaque réponse dans l’admin
  • Tester le branchement conditionnel en vérifiant que tous les chemins mènent à une fin
  • Valider les questions requises avant de passer à la suivante
  • Charger uniquement le quiz demandé par slug

A éviter

  • Utiliser l’ancien moteur V1 (getRecommendations) pour de nouveaux quiz
  • Stocker des données sensibles dans l’URL partageable (les réponses y sont encodées)
  • Oublier de configurer RESEND_API_KEY en production pour l’envoi d’email

Ressources

Admin Dashboard

Gestion complète des quiz et scoring weights

Database Schema

Structure des tables quiz avec scoring_weights

API Layer

Utilisation de l’API GraphQL

Custom Hooks

Documentation du hook useQuiz