This commit is contained in:
Tomáš Dvořák
2025-10-16 13:32:05 +02:00
commit 12cba639b9
663 changed files with 168914 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
package models
import (
"time"
)
// Poll represents a voting poll
type Poll struct {
ID uint `gorm:"primarykey" json:"id"`
Title string `gorm:"size:255;not null" json:"title"`
Description string `gorm:"type:text" json:"description"`
Type string `gorm:"size:50;not null;default:'single'" json:"type"` // single, multiple, rating
Status string `gorm:"size:20;not null;default:'draft'" json:"status"` // draft, active, closed, archived
StartDate *time.Time `json:"start_date"`
EndDate *time.Time `json:"end_date"`
AllowMultiple bool `gorm:"default:false" json:"allow_multiple"` // Allow voting for multiple options
MaxChoices int `gorm:"default:1" json:"max_choices"` // Max number of choices if allow_multiple
ShowResults string `gorm:"size:20;default:'after_vote'" json:"show_results"` // always, after_vote, after_end, never
RequireAuth bool `gorm:"default:false" json:"require_auth"` // Require login to vote
AllowGuestVote bool `gorm:"default:true" json:"allow_guest_vote"` // Allow anonymous voting
Featured bool `gorm:"default:false" json:"featured"` // Show on homepage
CategoryID *uint `json:"category_id"` // Optional category
Category *Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
RelatedMatchID *uint `json:"related_match_id"` // Link to specific match (for MOTM voting)
RelatedArticleID *uint `json:"related_article_id"` // Link to blog article
RelatedArticle *Article `gorm:"foreignKey:RelatedArticleID" json:"related_article,omitempty"`
RelatedEventID *uint `json:"related_event_id"` // Link to event/activity
RelatedEvent *Event `gorm:"foreignKey:RelatedEventID" json:"related_event,omitempty"`
RelatedVideoURL string `gorm:"size:500" json:"related_video_url"` // YouTube video URL/ID
ImageURL string `gorm:"size:500" json:"image_url"`
TotalVotes int `gorm:"default:0" json:"total_votes"`
Options []PollOption `gorm:"foreignKey:PollID;constraint:OnDelete:CASCADE" json:"options"`
Votes []PollVote `gorm:"foreignKey:PollID;constraint:OnDelete:CASCADE" json:"votes,omitempty"`
CreatedBy uint `json:"created_by"`
Creator *User `gorm:"foreignKey:CreatedBy" json:"creator,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `gorm:"index" json:"deleted_at,omitempty"`
}
// PollOption represents a single option in a poll
type PollOption struct {
ID uint `gorm:"primarykey" json:"id"`
PollID uint `gorm:"not null;index" json:"poll_id"`
Poll *Poll `gorm:"foreignKey:PollID" json:"poll,omitempty"`
Text string `gorm:"size:255;not null" json:"text"`
Description string `gorm:"type:text" json:"description"`
ImageURL string `gorm:"size:500" json:"image_url"`
DisplayOrder int `gorm:"default:0" json:"display_order"`
VoteCount int `gorm:"default:0" json:"vote_count"`
PlayerID *uint `json:"player_id"` // Optional link to player (for MOTM)
Player *Player `gorm:"foreignKey:PlayerID" json:"player,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// PollVote represents a user's vote
type PollVote struct {
ID uint `gorm:"primarykey" json:"id"`
PollID uint `gorm:"not null;index" json:"poll_id"`
Poll *Poll `gorm:"foreignKey:PollID" json:"poll,omitempty"`
OptionID uint `gorm:"not null;index" json:"option_id"`
Option *PollOption `gorm:"foreignKey:OptionID" json:"option,omitempty"`
UserID *uint `gorm:"index" json:"user_id"` // Null for guest votes
User *User `gorm:"foreignKey:UserID" json:"user,omitempty"`
IPHash string `gorm:"size:64;index" json:"ip_hash"` // Hashed IP for duplicate prevention
UserAgent string `gorm:"size:500" json:"user_agent"`
SessionToken string `gorm:"size:100;index" json:"session_token"` // For guest vote tracking
CreatedAt time.Time `json:"created_at"`
}
// TableName overrides the table name
func (Poll) TableName() string {
return "polls"
}
func (PollOption) TableName() string {
return "poll_options"
}
func (PollVote) TableName() string {
return "poll_votes"
}
// IsActive checks if poll is currently accepting votes
func (p *Poll) IsActive() bool {
if p.Status != "active" {
return false
}
now := time.Now()
if p.StartDate != nil && now.Before(*p.StartDate) {
return false
}
if p.EndDate != nil && now.After(*p.EndDate) {
return false
}
return true
}
// CanShowResults checks if results should be displayed
func (p *Poll) CanShowResults(hasVoted bool) bool {
switch p.ShowResults {
case "always":
return true
case "after_vote":
return hasVoted
case "after_end":
if p.EndDate != nil {
return time.Now().After(*p.EndDate)
}
return p.Status == "closed" || p.Status == "archived"
case "never":
return false
default:
return false
}
}