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"` Style string `gorm:"size:50;not null;default:'auto'" json:"style"` 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 VoterName string `gorm:"size:150" json:"voter_name"` VoterEmail string `gorm:"size:200" json:"voter_email"` 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 } }