mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
upload
This commit is contained in:
@@ -0,0 +1,363 @@
|
||||
# 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`
|
||||
```sql
|
||||
- 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`
|
||||
```sql
|
||||
- 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`
|
||||
```sql
|
||||
- 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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
{
|
||||
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
|
||||
|
||||
```typescript
|
||||
{
|
||||
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:
|
||||
|
||||
```tsx
|
||||
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:
|
||||
|
||||
```bash
|
||||
# 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
|
||||
Reference in New Issue
Block a user