Files
MyClub/DOCS/POLL_SYSTEM_IMPLEMENTATION.md
T
Tomáš Dvořák 12cba639b9 upload
2025-10-16 13:32:05 +02:00

9.9 KiB

Poll/Voting System Implementation

Overview

A comprehensive poll and voting system has been implemented for the football club website. This system allows administrators to create interactive polls and enables fans to participate in voting on various topics such as Man of the Match, match predictions, design choices, and more.

Features

Admin Features

  • Create and manage polls with an intuitive interface
  • Multiple poll types:
    • Single choice (radio buttons)
    • Multiple choice (checkboxes)
    • Rating system
  • Poll status management: Draft, Active, Closed, Archived
  • Scheduling: Set start and end dates for automatic poll activation/deactivation
  • Flexible settings:
    • Allow/require authentication for voting
    • Control when results are visible (always, after vote, after end, never)
    • Enable multiple choices with configurable max selections
    • Feature polls on homepage
  • Rich options: Add descriptions and images to poll options
  • Player integration: Link poll options to players (for MOTM voting)
  • Real-time statistics: View vote counts, percentages, and voting trends
  • Vote analytics: Track authenticated vs guest votes, votes by day

Public Features

  • Interactive voting interface with visual feedback
  • Real-time results display with progress bars
  • Guest voting with session tracking (no login required)
  • Duplicate vote prevention via IP hashing and session tokens
  • Mobile-responsive design
  • Featured polls widget for homepage integration

Database Schema

Tables

polls

- id (PRIMARY KEY)
- title (VARCHAR 255)
- description (TEXT)
- type (VARCHAR 50) - 'single', 'multiple', 'rating'
- status (VARCHAR 20) - 'draft', 'active', 'closed', 'archived'
- start_date (TIMESTAMP)
- end_date (TIMESTAMP)
- allow_multiple (BOOLEAN)
- max_choices (INTEGER)
- show_results (VARCHAR 20) - 'always', 'after_vote', 'after_end', 'never'
- require_auth (BOOLEAN)
- allow_guest_vote (BOOLEAN)
- featured (BOOLEAN)
- category_id (FK to categories)
- related_match_id (INTEGER)
- image_url (VARCHAR 500)
- total_votes (INTEGER)
- created_by (FK to users)
- created_at, updated_at, deleted_at

poll_options

- id (PRIMARY KEY)
- poll_id (FK to polls)
- text (VARCHAR 255)
- description (TEXT)
- image_url (VARCHAR 500)
- display_order (INTEGER)
- vote_count (INTEGER)
- player_id (FK to players)
- created_at, updated_at

poll_votes

- id (PRIMARY KEY)
- poll_id (FK to polls)
- option_id (FK to poll_options)
- user_id (FK to users, nullable)
- ip_hash (VARCHAR 64) - SHA256 hash for duplicate prevention
- user_agent (VARCHAR 500)
- session_token (VARCHAR 100) - For guest tracking
- created_at

API Endpoints

Public Endpoints

GET    /api/v1/polls                    Get active polls (optional ?featured=true)
GET    /api/v1/polls/:id                Get poll details with voting status
POST   /api/v1/polls/:id/vote           Submit a vote (rate limited)
GET    /api/v1/polls/:id/results        Get poll results (if allowed)

Admin Endpoints (Requires JWT + Admin Role)

GET    /api/v1/admin/polls              Get all polls (optional ?status=active)
GET    /api/v1/admin/polls/:id          Get poll details
POST   /api/v1/admin/polls              Create new poll
PUT    /api/v1/admin/polls/:id          Update poll
DELETE /api/v1/admin/polls/:id          Delete poll (soft delete)
GET    /api/v1/admin/polls/:id/stats    Get detailed statistics

Frontend Components

Admin Components

PollsAdminPage.tsx (/admin/ankety)

  • Full CRUD interface for poll management
  • Tabbed interface for basic info, options, and settings
  • Real-time statistics modal
  • Vote count and percentage displays
  • Status filtering

Public Components

PollCard.tsx

  • Interactive voting interface
  • Radio buttons for single choice
  • Checkboxes for multiple choice
  • Results display with progress bars
  • Responsive design

PollsWidget.tsx

  • Homepage widget for featured polls
  • Automatic poll loading
  • Configurable display count

PollsPage.tsx (/ankety)

  • Dedicated page for all polls
  • Tabbed interface: All, Active, Featured
  • Grid layout for multiple polls

Usage Examples

1. Create a Man of the Match Poll

// Admin creates poll after match
{
  title: "Hráč zápasu - Sparta vs Slavia",
  description: "Hlasujte pro nejlepšího hráče dnešního zápasu!",
  type: "single",
  status: "active",
  end_date: "2025-10-15T23:59:59",
  show_results: "after_vote",
  featured: true,
  related_match_id: 123,
  options: [
    { text: "Jan Novák", player_id: 45 },
    { text: "Petr Svoboda", player_id: 23 },
    { text: "Martin Dvořák", player_id: 12 }
  ]
}

2. Create a Match Prediction Poll

{
  title: "Předpověď výsledku: Sparta vs Slavia",
  description: "Jak tipujete dnešní derby?",
  type: "single",
  status: "active",
  start_date: "2025-10-14T00:00:00",
  end_date: "2025-10-14T17:00:00", // Before match starts
  show_results: "after_end",
  allow_guest_vote: true,
  featured: true,
  options: [
    { text: "Výhra Sparty", description: "1X" },
    { text: "Remíza", description: "X" },
    { text: "Výhra Slavie", description: "2X" }
  ]
}

3. Fan Engagement Poll

{
  title: "Jaký design dresu preferujete?",
  description: "Pomožte nám vybrat nový design domácího dresu!",
  type: "multiple",
  allow_multiple: true,
  max_choices: 2,
  show_results: "always",
  featured: true,
  options: [
    { text: "Klasický pruhovaný", image_url: "/designs/stripes.jpg" },
    { text: "Moderní minimalistický", image_url: "/designs/minimal.jpg" },
    { text: "Retro inspirovaný", image_url: "/designs/retro.jpg" }
  ]
}

Integration with Homepage

Add the polls widget to your homepage:

import PollsWidget from '../components/home/PollsWidget';

// In HomePage.tsx
<PollsWidget 
  featuredOnly={true}
  maxPolls={1}
  title="Hlasování"
/>

Security Features

Duplicate Vote Prevention

  1. Authenticated Users: Tracked by user_id
  2. Guest Users:
    • IP address hashing (SHA256 with salt)
    • Session token stored in localStorage
    • User agent fingerprinting

Rate Limiting

  • Voting endpoint limited to 10 requests per minute
  • Prevents vote spamming

Data Privacy

  • IP addresses are hashed (never stored in plain text)
  • GDPR compliant
  • Guest votes are anonymous

Configuration Options

Poll Settings

Setting Description Default
type single, multiple, rating single
status draft, active, closed, archived draft
allow_multiple Enable multi-select false
max_choices Max selections if multiple 1
show_results When to show results after_vote
require_auth Force login to vote false
allow_guest_vote Allow anonymous voting true
featured Show on homepage false

Result Visibility Options

  • always: Results visible to everyone at all times
  • after_vote: Results shown only after user has voted
  • after_end: Results visible only after poll ends
  • never: Results hidden to public (admin only)

Migration

Run the migration to create tables:

# Backend will automatically run migration on startup
# Or manually run:
make migrate

Testing Checklist

  • Create a poll in admin panel
  • Vote as authenticated user
  • Vote as guest user
  • Verify duplicate vote prevention
  • Check results display based on settings
  • Test poll scheduling (start/end dates)
  • Verify featured polls show on homepage
  • Test multiple choice polls
  • Check statistics in admin panel
  • Verify mobile responsiveness

Common Use Cases

1. Post-Match Voting

Create MOTM poll immediately after matches end. Set to close after 24 hours.

2. Weekly Fan Polls

Regular engagement polls about club topics, player preferences, etc.

3. Season Predictions

Long-running polls for season outcomes, tournament predictions.

4. Design Choices

Let fans vote on merchandise designs, kit colors, club initiatives.

5. Event Planning

Poll fans about preferred match times, event types, etc.

Troubleshooting

Users can't vote

  • Check poll status is "active"
  • Verify poll hasn't ended
  • Check if require_auth is true (login required)
  • Verify rate limiting hasn't been triggered

Results not showing

  • Check show_results setting
  • Ensure user has voted if setting is "after_vote"
  • Verify poll has ended if setting is "after_end"

Duplicate votes getting through

  • Session token should persist in localStorage
  • IP hashing uses consistent salt
  • Check browser cache/cookies not cleared

Future Enhancements

Potential additions for future versions:

  • Poll categories and filtering
  • Social media sharing of poll results
  • Email notifications for new featured polls
  • CSV export of vote data
  • Advanced analytics dashboard
  • Poll templates for common use cases
  • Automated poll creation (e.g., MOTM after each match)
  • Integration with match result imports
  • Poll cloning/duplication
  • Scheduled poll publishing
  • Multi-language poll support

Technical Notes

Performance Considerations

  • Poll votes are cached for 2 minutes on frontend
  • Database indexes on poll_id, user_id, ip_hash, and session_token
  • Vote count updates use atomic increment operations
  • Results calculated on-the-fly with caching

Scalability

  • Supports thousands of concurrent voters
  • Efficient query indexing
  • Minimal database load with cached results
  • Stateless session tokens for horizontal scaling

Support

For questions or issues with the poll system:

  1. Check this documentation
  2. Review code comments in controllers and models
  3. Check admin panel for configuration options
  4. Verify database migrations have run

Implementation Date: October 2025
Version: 1.0
Last Updated: October 12, 2025