# Real-time Polling Application

**Quick Summary:** Build a complete Kahoot-style application for creating and joining polls with real-time voting and animated bar chart results.

---

## 1. Project Architecture

1. Create two folders:
   - **frontend**: for the user interface
   - **backend**: for database and routes

### Backend organization
1. Create a **tables** folder for data structures
2. Create a **services** folder for API routes

## 2. Poll Creation Page

### Create the CreatePoll page
1. Create a new page **CreatePoll**
2. Create a main container:
   - **Width**: 100%
   - **Height**: 100vh
   - **Background**: blue color

### Content structure
1. Add a content block with:
   - A **custom text** for the title
   - A blue block containing:
      - 1 **text input** for the question
      - 4 **text input** for answer choices
      - 1 **button** to validate

## 3. Database - Polls Table

### Create the table
1. In the **tables** folder, create a **Polls** table
2. Define columns:
   - **question** (string)
   - **answer1** (string)
   - **answer2** (string)
   - **answer3** (string)
   - **answer4** (string)
   - **gameId** (string)

> **gameId**: 5-character identifier for easy input, like in Kahoot

## 4. Poll Creation Route

### Create the createPolls route
1. In **services**, create a **Poll** folder
2. Create a **createPolls** route (POST method)
3. Define the **body** with same fields as the table

### Insertion logic
1. Create a **dbinsert** node
2. Select the **Polls** table
3. Connect **body** to **data** field

## 5. Variables and Frontend Binding

### Create page variables
1. In CreatePoll, create 5 variables:
   - **question** (string)
   - **answer1** (string)
   - **answer2** (string)
   - **answer3** (string)
   - **answer4** (string)

### Bind inputs
1. For each text input, set the corresponding **model**:
   - First input → **question**
   - Following inputs → **answer1**, **answer2**, **answer3**, **answer4**

## 6. Creation Logic with UUID

### Button onClick event
1. Add an **onClick** event on the validation button
2. Call the **createPolls** route

### Generate the gameId
1. Pull a wire from body to create a **create object**
2. Connect variables:
   - **get question** → question
   - **get answer1** → answer1
   - **get answer2** → answer2
   - **get answer3** → answer3
   - **get answer4** → answer4
3. For the **gameId**:
   - Go to **Library** → check **Utils**
   - Use **UUID** to generate a unique identifier
   - Add a **slice** (0, 5) to keep only first 5 characters
   - Connect to gameId field

> Test: fill fields → validate → check in Polls table

## 7. Home Page

### Create the Home page
1. Create a new **Home** page
2. Similar design to CreatePoll with:
   - An input for entering **gameId**
   - A button to join a poll
   - A "Create Your Own Poll" button to access CreatePoll

### Set as home page
1. Change CreatePoll's **path** so Home is the default page

### Navigation to CreatePoll
1. On "Create Your Own Poll" button, add **onClick** event
2. Create a **get router**
3. Add a **get property** → select **push**
4. Create a **lambda function**
5. Define path: `/createpoll`
6. Connect everything

> Test: click button → redirect to CreatePoll

## 8. Retrieve Poll by ID

### Create the getPollsBySlug route
1. In **services/Poll**, create **getPollsBySlug** route
2. Method: **GET**
3. Define **body**: **gameId** (string)

### Retrieval logic
1. Create a **dbfind** on **Polls** table
2. Add a **filter** on **gameId**
3. Retrieve gameId from **body** (get property)
4. Add **first** after dbfind to get an object instead of array
5. Connect to **response**

> Test with Execute: send gameId → retrieve poll object

## 9. Join Poll from Home

### Variables and binding
1. In Home, create a **gameId** variable
2. Bind text input to **model** gameId

### Validation event
1. On "Validate" button, add **onClick** event
2. Call **getPollsBySlug**
3. Send **gameId** in body

### Conditional redirect
1. Retrieve **response**
2. Cast response to **boolean** (if response exists = true)
3. Create **get router** → **push**
4. Create path with **concatenation**:
   - `/poll/` + gameId
5. Connect conditionally

> Test: invalid gameId = nothing / valid gameId = redirect to `/poll/{gameId}`

## 10. Poll Page (Polls)

### Create the page
1. Create a new **Polls** page
2. Define **path**: `/poll/:id`
3. Design with 3 zones:
   - Top zone: poll question
   - Center zone: results (vote bars)
   - Bottom zone: answer buttons

## 11. Store for Poll Data

### Create CurrentPoll store
1. Create a new store **CurrentPoll**
2. Define schema:
```
poll (object)
  ├─ question (string)
  ├─ answer1 (string)
  ├─ answer2 (string)
  ├─ answer3 (string)
  ├─ answer4 (string)
  └─ gameId (string)
```

### Fill store from Home
1. In Home logic (after getPollsBySlug)
2. Create a **get CurrentPoll**
3. Create a **set property** → select **poll**
4. Connect **response** from getPollsBySlug

> Advantage: avoids a new backend call from Polls page

## 12. Display Question

### Dynamic property
1. In question's custom text on Polls page
2. Write `{question}` to create property
3. Define **question** = `CurrentPoll.poll.question`

> Test: join a poll → question displays

## 13. ButtonVote Component

### Create the component
1. Create a new component **ButtonVote**
2. Define properties:
   - **value** (string): answer text
   - **index** (number): answer position (0-3)
3. Set button **content** = **value**

### Style the button
1. Add appropriate styles (padding, background, border-radius, etc.)

## 14. Display Answer Buttons

### Template to iterate over answers
1. In button zone, create a **template**
2. Logic: **for**
3. Iterate on: `CurrentPoll.poll` transformed to array
   - Create array with `[answer1, answer2, answer3, answer4]`

### Instantiate component
1. Add **ButtonVote** component in template
2. Define properties:
   - **value**: `template.value`
   - **index**: `template.index`

> All 4 buttons display automatically

## 15. Database - Votes Table

### Create the table
1. In **tables**, create a **Votes** table
2. Define columns:
   - **gameId** (string)
   - **voteIndex** (number): index of voted answer (0-3)

## 16. Vote Creation Route

### Create createVote route
1. In **services**, create a **Vote** folder
2. Create a **createVote** route (POST method)
3. Define **body**:
   - **voteIndex** (number)
   - **gameId** (string)

### Insertion and retrieval logic
1. Create a **dbinsert** on **Votes**
2. Connect **body** to **data**
3. After insertion, create a **dbfind** on **Votes**
4. Add a **filter** on **gameId** (retrieved from body with get property)
5. Connect to **response**

> Response returns all votes for the poll

## 17. Vote Logic in ButtonVote

### onClick event
1. In **ButtonVote** component, add **onClick** event
2. Call **createVote** route
3. Create **body**:
   - **voteIndex**: **get index** (component property)
   - **gameId**: `CurrentPoll.poll.gameId`

> Test: click button → new vote added to Votes table

## 18. Results Calculation Route

### Create getVotesByGameId route
1. In **services/Vote**, create **getVotesByGameId** (GET)
2. **Body**: **gameId** (string)
3. **Expected response**:
   - **votes**: array of 4 numbers (vote count per answer)
   - **totalVotes**: total vote count
   - **percentages**: array of 4 percentages

### Retrieve votes
1. Create a **dbfind** on **Votes**
2. Add a **filter** on **gameId**

### Install ES-Toolkit package
1. Go to **Library**
2. Install **ES-Toolkit** package

### Group votes with countBy
1. Use **countBy** from ES-Toolkit
2. Parameters:
   - List: dbfind result
   - Grouping function: `item.voteIndex`
3. Cast item to indicate it has **voteIndex** field
4. Use **get property** to extract object

> Result: object like `{0: 2, 1: 5, 2: 1, 3: 3}` (vote count per index)

### Transform to array
1. Create a **new array** of 4 numbers
2. Name indices: 0, 1, 2, 3
3. Use **spread** on countBy object
4. Connect each value to corresponding index

### Calculate total
1. Use **get length** on dbfind result

### Calculate percentages
1. Create a **map** on votes array
2. For each element:
   - Divide by **totalVotes**
   - Multiply by 100
3. Connect to response

### Structure response
1. Create a **create object** with:
   - **votes**: votes array
   - **totalVotes**: total count
   - **percentages**: percentages array

## 19. Store for Results

### Add to CurrentPoll store
```
votes (array of objects) - 4 elements
  └─ percentage (string)
  
totalVotes (number)
```

### Initialize store
1. Click "+" 4 times to create 4 empty objects in votes
2. Reset store

## 20. SetVotes Function in Store

### Create the function
1. In **CurrentPoll** store, create a **SetVotes** function
2. This function updates results after each vote

### Function logic
1. Call **getVotesByGameId**
2. Create **body** with store's gameId (`CurrentPoll.poll.gameId`)

### Update percentages
1. Create a **map** on `CurrentPoll.votes`
2. For each element:
   - Create a **set property** for **percentage**
   - Retrieve value with **spread** from response
   - Use **at** with index (check "index" in map)
   - Cast index to **string**
   - **Concatenate** with `%` symbol

### Update votes
1. Create another **set property** for **votes**
2. Same logic: spread → at → index

### Update totalVotes
1. Create a **set property** for **totalVotes**
2. Connect from `response.totalVotes`

### Finalize
1. Connect map to function **output**
2. Click **Export**

> Optimization possibility: break down into multiple functions for readability

## 21. Call SetVotes After Vote

### Modify ButtonVote
1. In ButtonVote's onClick logic
2. After **createVote** call
3. Call store's **SetVotes** function

> Now each vote automatically updates results

## 22. Display Result Bars

### Create bars template
1. In results zone, create a **template**
2. Logic: **for**
3. Iterate on: `CurrentPoll.votes`

### Style the container
1. Enlarge container to see bars
2. Define styles:
   - **Display**: flex
   - **Align-items**: flex-end (bottom alignment)
   - **Justify-content**: space-around

### Create bars
1. In template, create a block (the bar)
2. Define height dynamically:
   - **Height**: `template.value.percentage` (e.g., "25%")
3. Add centered **custom text** to display vote count
4. Add **transition** for animation

> Test: vote → bars animate in real-time!

## Summary of Key Concepts

| Concept | Usage |
|---------|-------|
| **UUID + slice** | Generate short identifiers (5 characters) |
| **Router push** | Programmatic navigation between pages |
| **Path parameters** | Dynamic URL with `:id` |
| **Store functions** | Encapsulate update logic |
| **countBy (ES-Toolkit)** | Group and count elements |
| **Map** | Transform arrays |
| **Template (for)** | Iterate to display lists |
| **Concatenation** | Build dynamic URLs and strings |
| **Cast to boolean** | Check response existence |
| **CSS Transitions** | Smooth animations |

## Complete Architecture

```
Frontend
├─ Home (home page)
│   ├─ gameId input
│   ├─ "Join Poll" button
│   └─ "Create Your Own Poll" button
├─ CreatePoll (creation)
│   ├─ Question input
│   ├─ 4 answer inputs
│   └─ Validation button
└─ Polls (active poll)
    ├─ Question
    ├─ Result bars
    └─ Vote buttons (ButtonVote component)

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)
```

## Practical Tips

- **5-character gameId**: slice(0, 5) on UUID for simplicity
- **Store functions**: centralize complex logic
- Use **ES-Toolkit** for advanced array operations
- **Path parameters**: `:id` in URL for dynamic pages
- **CSS animations**: add transitions for professional visual effects
- **Cast to boolean**: transform response to condition (exists = true)
- **Map with index**: check "index" to access position
- Always **reset store** after schema modification
- **Concatenation**: to build dynamic URLs
- Remember to **export** store functions

---
