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 desscoring_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.
tool:<id>— Points directs pour un outil spécifiquecategory:<name>— Points pour tous les outils d’une catégoriepricing:<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 quizLa 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.Moteur de scoring V2
calculateScores()
Fichier :src/api/quiz/scoring.ts
- Initialise un score de 0 pour chaque outil du catalogue
- Pour chaque réponse de l’utilisateur, trouve les
scoring_weightscorrespondants - Pour chaque clé de poids :
tool:<id>→ ajoute les points directement à l’outilcategory:<name>→ ajoute les points à tous les outils de la catégoriepricing:<tier>→ ajoute les points à tous les outils du tier
- Accumule les “raisons” (texte de la question + réponse) pour expliquer le score
- Filtre les outils avec score > 0, trie par score décroissant
- Retourne les
maxToolsmeilleurs 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 champnext_question_rules :
next_question_rules est null, le quiz avance séquentiellement (par order_index).
Navigation
Fichier :src/features/quiz/hooks/useQuiz.ts
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 :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 -> ResultsQuizResults — 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 :
- Appelle
quizApi.getRecommendationsByScoring()avec les réponses - Stocke le
QuizReportdans le state - Met à jour l’URL avec le lien partageable
- 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
API Layer (GraphQL)
Fichier :src/api/quiz/index.ts
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_weightssur 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_KEYen 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