# Application de sondage en temps réel

**Résumé express :** Créer une application complète type Kahoot permettant de créer et rejoindre des sondages avec votes en temps réel et affichage des résultats en barres animées.

---

## 1. Architecture du projet

1. Créer deux dossiers :
   - **frontend** : pour l'interface utilisateur
   - **backend** : pour la base de données et les routes

### Organisation du backend
1. Créer un dossier **tables** pour les structures de données
2. Créer un dossier **services** pour les routes API

## 2. Page de création de sondage

### Créer la page CreatePoll
1. Créer une nouvelle page **CreatePoll**
2. Créer un conteneur principal :
   - **Width** : 100%
   - **Height** : 100vh
   - **Background** : couleur bleue

### Structure du contenu
1. Ajouter un bloc de contenu avec :
   - Un **custom text** pour le titre
   - Un bloc bleu contenant :
      - 1 **text input** pour la question
      - 4 **text input** pour les choix de réponses
      - 1 **button** pour valider

## 3. Base de données - Table Polls

### Créer la table
1. Dans le dossier **tables**, créer une table **Polls**
2. Définir les colonnes :
   - **question** (string)
   - **answer1** (string)
   - **answer2** (string)
   - **answer3** (string)
   - **answer4** (string)
   - **gameId** (string)

> **gameId** : identifiant à 5 caractères pour faciliter la saisie, comme dans Kahoot

## 4. Route de création de sondage

### Créer la route createPolls
1. Dans **services**, créer un dossier **Poll**
2. Créer une route **createPolls** (méthode POST)
3. Définir le **body** avec les mêmes champs que la table

### Logique d'insertion
1. Créer un node **dbinsert**
2. Sélectionner la table **Polls**
3. Connecter le **body** au champ **data**

## 5. Variables et liaison frontend

### Créer les variables de page
1. Dans CreatePoll, créer 5 variables :
   - **question** (string)
   - **answer1** (string)
   - **answer2** (string)
   - **answer3** (string)
   - **answer4** (string)

### Lier les inputs
1. Pour chaque text input, définir le **model** correspondant :
   - Premier input → **question**
   - Inputs suivants → **answer1**, **answer2**, **answer3**, **answer4**

## 6. Logique de création avec UUID

### Événement onClick du bouton
1. Ajouter un événement **onClick** sur le bouton de validation
2. Appeler la route **createPolls**

### Générer le gameId
1. Tirer un fil depuis le body pour créer un **create object**
2. Connecter les variables :
   - **get question** → question
   - **get answer1** → answer1
   - **get answer2** → answer2
   - **get answer3** → answer3
   - **get answer4** → answer4
3. Pour le **gameId** :
   - Aller dans **Librairie** → cocher **Utils**
   - Utiliser **UUID** pour générer un identifiant unique
   - Ajouter un **slice** (0, 5) pour ne garder que les 5 premiers caractères
   - Connecter au champ gameId

> Tester : remplir les champs → valider → vérifier dans la table Polls

## 7. Page d'accueil (Home)

### Créer la page Home
1. Créer une nouvelle page **Home**
2. Design similaire à CreatePoll avec :
   - Un input pour entrer le **gameId**
   - Un bouton pour rejoindre un sondage
   - Un bouton "Create Your Own Poll" pour accéder à CreatePoll

### Définir comme page d'accueil
1. Changer le **path** de CreatePoll pour que Home soit la page par défaut

### Navigation vers CreatePoll
1. Sur le bouton "Create Your Own Poll", ajouter un événement **onClick**
2. Créer un **get router**
3. Ajouter un **get property** → sélectionner **push**
4. Créer une **fonction lambda**
5. Définir le chemin : `/createpoll`
6. Connecter le tout

> Tester : cliquer sur le bouton → redirection vers CreatePoll

## 8. Récupération d'un sondage par ID

### Créer la route getPollsBySlug
1. Dans **services/Poll**, créer une route **getPollsBySlug**
2. Méthode : **GET**
3. Définir le **body** : **gameId** (string)

### Logique de récupération
1. Créer un **dbfind** sur la table **Polls**
2. Ajouter un **filter** sur **gameId**
3. Récupérer le gameId depuis le **body** (get property)
4. Ajouter un **first** après le dbfind pour obtenir un objet au lieu d'un tableau
5. Connecter à la **réponse**

> Tester avec Execute : envoyer un gameId → récupérer l'objet du sondage

## 9. Rejoindre un sondage depuis Home

### Variables et liaison
1. Dans Home, créer une variable **gameId**
2. Lier le text input au **model** gameId

### Événement de validation
1. Sur le bouton "Validate", ajouter un événement **onClick**
2. Appeler **getPollsBySlug**
3. Envoyer le **gameId** dans le body

### Redirection conditionnelle
1. Récupérer la **réponse**
2. Caster la réponse en **boolean** (si réponse existe = true)
3. Créer un **get router** → **push**
4. Créer le chemin avec **concatenation** :
   - `/poll/` + gameId
5. Connecter conditionnellement

> Tester : gameId invalide = rien / gameId valide = redirection vers `/poll/{gameId}`

## 10. Page de sondage (Polls)

### Créer la page
1. Créer une nouvelle page **Polls**
2. Définir le **path** : `/poll/:id`
3. Design avec 3 zones :
   - Zone supérieure : question du sondage
   - Zone centrale : résultats (barres de votes)
   - Zone inférieure : boutons de réponses

## 11. Store pour les données du sondage

### Créer le store CurrentPoll
1. Créer un nouveau store **CurrentPoll**
2. Définir le schéma :
```
poll (object)
  ├─ question (string)
  ├─ answer1 (string)
  ├─ answer2 (string)
  ├─ answer3 (string)
  ├─ answer4 (string)
  └─ gameId (string)
```

### Remplir le store depuis Home
1. Dans la logique de Home (après getPollsBySlug)
2. Créer un **get CurrentPoll**
3. Créer un **set property** → sélectionner **poll**
4. Connecter la **réponse** de getPollsBySlug

> Avantage : évite un nouvel appel au backend depuis la page Polls

## 12. Affichage de la question

### Propriété dynamique
1. Dans le custom text de la question sur la page Polls
2. Écrire `{question}` pour créer la propriété
3. Définir **question** = `CurrentPoll.poll.question`

> Tester : rejoindre un sondage → la question s'affiche

## 13. Composant ButtonVote

### Créer le composant
1. Créer un nouveau composant **ButtonVote**
2. Définir les propriétés :
   - **value** (string) : texte de la réponse
   - **index** (number) : position de la réponse (0-3)
3. Définir le **contenu** du bouton = **value**

### Styliser le bouton
1. Ajouter les styles appropriés (padding, background, border-radius, etc.)

## 14. Affichage des boutons de réponse

### Template pour itérer sur les réponses
1. Dans la zone des boutons, créer un **template**
2. Logique : **for**
3. Itérer sur : `CurrentPoll.poll` en transformant en tableau
   - Créer un tableau avec `[answer1, answer2, answer3, answer4]`

### Instancier le composant
1. Ajouter le composant **ButtonVote** dans le template
2. Définir les propriétés :
   - **value** : `template.value`
   - **index** : `template.index`

> Les 4 boutons s'affichent automatiquement

## 15. Base de données - Table Votes

### Créer la table
1. Dans **tables**, créer une table **Votes**
2. Définir les colonnes :
   - **gameId** (string)
   - **voteIndex** (number) : index de la réponse votée (0-3)

## 16. Route de création de vote

### Créer la route createVote
1. Dans **services**, créer un dossier **Vote**
2. Créer une route **createVote** (méthode POST)
3. Définir le **body** :
   - **voteIndex** (number)
   - **gameId** (string)

### Logique d'insertion et récupération
1. Créer un **dbinsert** sur **Votes**
2. Connecter le **body** à **data**
3. Après insertion, créer un **dbfind** sur **Votes**
4. Ajouter un **filter** sur **gameId** (récupéré du body avec get property)
5. Connecter à la **réponse**

> La réponse renvoie tous les votes du sondage

## 17. Logique de vote dans ButtonVote

### Événement onClick
1. Dans le composant **ButtonVote**, ajouter un événement **onClick**
2. Appeler la route **createVote**
3. Créer le **body** :
   - **voteIndex** : **get index** (propriété du composant)
   - **gameId** : `CurrentPoll.poll.gameId`

> Tester : cliquer sur un bouton → nouveau vote ajouté dans la table Votes

## 18. Route de calcul des résultats

### Créer la route getVotesByGameId
1. Dans **services/Vote**, créer **getVotesByGameId** (GET)
2. **Body** : **gameId** (string)
3. **Réponse attendue** :
   - **votes** : tableau de 4 nombres (nombre de votes par réponse)
   - **totalVotes** : nombre total de votes
   - **percentages** : tableau de 4 pourcentages

### Récupérer les votes
1. Créer un **dbfind** sur **Votes**
2. Ajouter un **filter** sur **gameId**

### Installer le package ES-Toolkit
1. Aller dans **Librairie**
2. Installer le package **ES-Toolkit**

### Regrouper les votes avec countBy
1. Utiliser **countBy** de ES-Toolkit
2. Paramètres :
   - Liste : résultat du dbfind
   - Fonction de regroupement : `item.voteIndex`
3. Caster l'item pour indiquer qu'il a un champ **voteIndex**
4. Faire un **get property** pour extraire l'objet

> Résultat : objet de type `{0: 2, 1: 5, 2: 1, 3: 3}` (nombre de votes par index)

### Transformer en tableau
1. Créer un **new array** de 4 nombres
2. Nommer les indices : 0, 1, 2, 3
3. Utiliser **spread** sur l'objet countBy
4. Connecter chaque valeur à l'index correspondant

### Calculer le total
1. Utiliser **get length** sur le résultat du dbfind

### Calculer les pourcentages
1. Créer un **map** sur le tableau de votes
2. Pour chaque élément :
   - Diviser par **totalVotes**
   - Multiplier par 100
3. Connecter à la réponse

### Structurer la réponse
1. Créer un **create object** avec :
   - **votes** : tableau de votes
   - **totalVotes** : nombre total
   - **percentages** : tableau de pourcentages

## 19. Store pour les résultats

### Ajouter au store CurrentPoll
```
votes (array of objects) - 4 éléments
  └─ percentage (string)
  
totalVotes (number)
```

### Initialiser le store
1. Cliquer 4 fois sur le "+" pour créer 4 objets vides dans votes
2. Reset le store

## 20. Fonction SetVotes dans le store

### Créer la fonction
1. Dans le store **CurrentPoll**, créer une fonction **SetVotes**
2. Cette fonction met à jour les résultats après chaque vote

### Logique de la fonction
1. Appeler **getVotesByGameId**
2. Créer le **body** avec le gameId du store (`CurrentPoll.poll.gameId`)

### Mettre à jour les pourcentages
1. Créer un **map** sur `CurrentPoll.votes`
2. Pour chaque élément :
   - Créer un **set property** pour **percentage**
   - Récupérer la valeur avec **spread** de la réponse
   - Utiliser **at** avec l'index (cocher "index" dans le map)
   - Caster l'index en **string**
   - **Concatener** avec le symbole `%`

### Mettre à jour les votes
1. Créer un autre **set property** pour **votes**
2. Même logique : spread → at → index

### Mettre à jour totalVotes
1. Créer un **set property** pour **totalVotes**
2. Connecter depuis `response.totalVotes`

### Finaliser
1. Connecter le map à l'**output** de la fonction
2. Cliquer sur **Export**

> Possibilité d'optimisation : décomposer en plusieurs fonctions pour plus de lisibilité

## 21. Appeler SetVotes après le vote

### Modifier ButtonVote
1. Dans la logique onClick de ButtonVote
2. Après l'appel à **createVote**
3. Appeler la fonction **SetVotes** du store

> Maintenant, chaque vote met à jour automatiquement les résultats

## 22. Affichage des barres de résultats

### Créer le template des barres
1. Dans la zone des résultats, créer un **template**
2. Logique : **for**
3. Itérer sur : `CurrentPoll.votes`

### Styliser le conteneur
1. Agrandir le conteneur pour voir les barres
2. Définir les styles :
   - **Display** : flex
   - **Align-items** : flex-end (alignement en bas)
   - **Justify-content** : space-around

### Créer les barres
1. Dans le template, créer un bloc (la barre)
2. Définir la hauteur dynamiquement :
   - **Height** : `template.value.percentage` (ex: "25%")
3. Ajouter un **custom text** centré pour afficher le nombre de votes
4. Ajouter une **transition** pour l'animation

> Tester : voter → les barres s'animent en temps réel !

## Récapitulatif des concepts clés

| Concept | Utilisation |
|---------|-------------|
| **UUID + slice** | Générer des identifiants courts (5 caractères) |
| **Router push** | Navigation programmatique entre pages |
| **Path parameters** | URL dynamique avec `:id` |
| **Store functions** | Encapsuler la logique de mise à jour |
| **countBy (ES-Toolkit)** | Regrouper et compter des éléments |
| **Map** | Transformer des tableaux |
| **Template (for)** | Itérer pour afficher des listes |
| **Concatenation** | Construire des URLs et strings dynamiques |
| **Cast to boolean** | Vérifier l'existence d'une réponse |
| **Transitions CSS** | Animations fluides |

## Architecture complète

```
Frontend
├─ Home (page d'accueil)
│   ├─ Input gameId
│   ├─ Bouton "Join Poll"
│   └─ Bouton "Create Your Own Poll"
├─ CreatePoll (création)
│   ├─ Input question
│   ├─ 4 inputs réponses
│   └─ Bouton validation
└─ Polls (sondage actif)
    ├─ Question
    ├─ Barres de résultats
    └─ Boutons de vote (composant ButtonVote)

Backend
├─ Tables
│   ├─ Polls (question, answer1-4, gameId)
│   └─ Votes (gameId, voteIndex)
└─ Services
    ├─ Poll
    │   ├─ createPolls
    │   └─ getPollsBySlug
    └─ Vote
        ├─ createVote
        └─ getVotesByGameId

Store
└─ CurrentPoll
    ├─ poll (object)
    ├─ votes (array)
    ├─ totalVotes (number)
    └─ SetVotes() (function)
```

## Conseils pratiques

- **gameId à 5 caractères** : slice(0, 5) sur UUID pour simplicité
- **Store functions** : centraliser la logique complexe
- Utiliser **ES-Toolkit** pour des opérations avancées sur les tableaux
- **Path parameters** : `:id` dans l'URL pour les pages dynamiques
- **Animations CSS** : ajouter des transitions pour un effet visuel professionnel
- **Cast to boolean** : transformer une réponse en condition (existe = true)
- **Map avec index** : cocher "index" pour accéder à la position
- Toujours **reset le store** après modification du schéma
- **Concatenation** : pour construire des URLs dynamiques
- Penser à **exporter** les fonctions du store

---
