mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 10:42:57 +00:00
dev day #70
This commit is contained in:
@@ -129,6 +129,7 @@ func (pc *PollController) CreatePoll(c *gin.Context) {
|
||||
Title string `json:"title" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Type string `json:"type"`
|
||||
Style string `json:"style"`
|
||||
Status string `json:"status"`
|
||||
StartDate *time.Time `json:"start_date"`
|
||||
EndDate *time.Time `json:"end_date"`
|
||||
@@ -166,6 +167,7 @@ func (pc *PollController) CreatePoll(c *gin.Context) {
|
||||
Title: input.Title,
|
||||
Description: input.Description,
|
||||
Type: input.Type,
|
||||
Style: input.Style,
|
||||
Status: input.Status,
|
||||
StartDate: input.StartDate,
|
||||
EndDate: input.EndDate,
|
||||
@@ -188,6 +190,9 @@ func (pc *PollController) CreatePoll(c *gin.Context) {
|
||||
if poll.Type == "" {
|
||||
poll.Type = "single"
|
||||
}
|
||||
if poll.Style == "" {
|
||||
poll.Style = "auto"
|
||||
}
|
||||
if poll.Status == "" {
|
||||
poll.Status = "draft"
|
||||
}
|
||||
@@ -250,6 +255,7 @@ func (pc *PollController) UpdatePoll(c *gin.Context) {
|
||||
Title *string `json:"title"`
|
||||
Description *string `json:"description"`
|
||||
Type *string `json:"type"`
|
||||
Style *string `json:"style"`
|
||||
Status *string `json:"status"`
|
||||
StartDate *time.Time `json:"start_date"`
|
||||
EndDate *time.Time `json:"end_date"`
|
||||
@@ -282,6 +288,9 @@ func (pc *PollController) UpdatePoll(c *gin.Context) {
|
||||
if input.Type != nil {
|
||||
poll.Type = *input.Type
|
||||
}
|
||||
if input.Style != nil {
|
||||
poll.Style = *input.Style
|
||||
}
|
||||
if input.Status != nil {
|
||||
poll.Status = *input.Status
|
||||
}
|
||||
|
||||
@@ -144,36 +144,37 @@ func averageHex(img image.Image) string {
|
||||
return fmt.Sprintf("#%02x%02x%02x", r8, g8, b8)
|
||||
}
|
||||
|
||||
// SwapSides swaps home and away team info including names, logos, shorts, scores and colors.
|
||||
// SwapSides toggles visual sides flipping only. It does NOT swap team data.
|
||||
func (c *ScoreboardController) SwapSides(ctx *gin.Context) {
|
||||
s, err := c.getOrCreateSingleton()
|
||||
if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": "cannot load scoreboard"}); return }
|
||||
s.HomeName, s.AwayName = s.AwayName, s.HomeName
|
||||
s.HomeLogoURL, s.AwayLogoURL = s.AwayLogoURL, s.HomeLogoURL
|
||||
s.HomeScore, s.AwayScore = s.AwayScore, s.HomeScore
|
||||
s.HomeShort, s.AwayShort = s.AwayShort, s.HomeShort
|
||||
s.PrimaryColor, s.SecondaryColor = s.SecondaryColor, s.PrimaryColor
|
||||
s.SidesFlipped = !s.SidesFlipped
|
||||
if err := c.DB.Save(s).Error; err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save"}); return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, gin.H{"ok": true})
|
||||
}
|
||||
|
||||
// StartSecondHalf swaps sides, resets the timer to 00:00 and immediately starts it.
|
||||
// StartSecondHalf starts the second half without flipping visual sides and continues timer from end of 1st half.
|
||||
func (c *ScoreboardController) StartSecondHalf(ctx *gin.Context) {
|
||||
s, err := c.getOrCreateSingleton()
|
||||
if err != nil { ctx.JSON(http.StatusInternalServerError, gin.H{"error": "cannot load scoreboard"}); return }
|
||||
// swap first
|
||||
s.HomeName, s.AwayName = s.AwayName, s.HomeName
|
||||
s.HomeLogoURL, s.AwayLogoURL = s.AwayLogoURL, s.HomeLogoURL
|
||||
s.HomeScore, s.AwayScore = s.AwayScore, s.HomeScore
|
||||
s.HomeShort, s.AwayShort = s.AwayShort, s.HomeShort
|
||||
s.PrimaryColor, s.SecondaryColor = s.SecondaryColor, s.PrimaryColor
|
||||
// reset and start timer for next half
|
||||
// Move to second half and continue from end of first half
|
||||
s.Half = 2
|
||||
// Ensure base elapsed reflects end of first half
|
||||
capFirst := s.HalfLength * 60
|
||||
if capFirst <= 0 { capFirst = 45 * 60 }
|
||||
base := s.ElapsedSeconds
|
||||
if s.Running && s.TimerStartUnix > 0 {
|
||||
now := time.Now().Unix()
|
||||
diff := int(now - s.TimerStartUnix)
|
||||
if diff > base { base = diff }
|
||||
}
|
||||
if base < capFirst { base = capFirst }
|
||||
s.ElapsedSeconds = base
|
||||
s.Timer = formatSeconds(base)
|
||||
s.Running = true
|
||||
s.ElapsedSeconds = 0
|
||||
s.TimerStartUnix = time.Now().Unix()
|
||||
s.Timer = "00:00"
|
||||
s.TimerStartUnix = time.Now().Unix() - int64(base)
|
||||
if err := c.DB.Save(s).Error; err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save"}); return
|
||||
}
|
||||
@@ -260,6 +261,10 @@ func applyImportedState(imported models.ScoreboardState, c *ScoreboardController
|
||||
if imported.SecondaryColor != "" { s.SecondaryColor = imported.SecondaryColor }
|
||||
s.HomeScore = imported.HomeScore
|
||||
s.AwayScore = imported.AwayScore
|
||||
// fouls with clamping
|
||||
clamp := func(v int) int { if v < 0 { return 0 }; if v > 5 { return 5 }; return v }
|
||||
s.HomeFouls = clamp(imported.HomeFouls)
|
||||
s.AwayFouls = clamp(imported.AwayFouls)
|
||||
if imported.HalfLength > 0 { s.HalfLength = imported.HalfLength }
|
||||
if imported.Theme != "" { s.Theme = imported.Theme }
|
||||
// timer handling
|
||||
@@ -321,9 +326,10 @@ func computeTimer(s models.ScoreboardState) (timer string, running bool) {
|
||||
if diff > 0 { base = diff } else { base = 0 }
|
||||
}
|
||||
}
|
||||
// Cap by half length
|
||||
// Cap by half length; allow up to 2*half when second half is active
|
||||
cap := s.HalfLength * 60
|
||||
if cap <= 0 { cap = 45 * 60 }
|
||||
if s.Half >= 2 { cap = s.HalfLength * 120 }
|
||||
if base >= cap {
|
||||
base = cap
|
||||
running = false
|
||||
@@ -411,6 +417,8 @@ func (c *ScoreboardController) getOrCreateSingleton() (*models.ScoreboardState,
|
||||
Half: 1,
|
||||
QRShowEveryMinutes: 5,
|
||||
QRShowDurationSeconds: 60,
|
||||
HomeFouls: 0,
|
||||
AwayFouls: 0,
|
||||
}
|
||||
if err := c.DB.Create(&s).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -424,6 +432,12 @@ func (c *ScoreboardController) getOrCreateSingleton() (*models.ScoreboardState,
|
||||
if s.Half == 0 { s.Half = 1; changed = true }
|
||||
if s.QRShowEveryMinutes == 0 { s.QRShowEveryMinutes = 5; changed = true }
|
||||
if s.QRShowDurationSeconds == 0 { s.QRShowDurationSeconds = 60; changed = true }
|
||||
// Clamp fouls 0..5 and ensure non-negative
|
||||
clamp := func(v int) int { if v < 0 { return 0 }; if v > 5 { return 5 }; return v }
|
||||
nf := clamp(s.HomeFouls)
|
||||
af := clamp(s.AwayFouls)
|
||||
if s.HomeFouls != nf { s.HomeFouls = nf; changed = true }
|
||||
if s.AwayFouls != af { s.AwayFouls = af; changed = true }
|
||||
if changed { _ = c.DB.Save(&s).Error }
|
||||
return &s, nil
|
||||
}
|
||||
@@ -447,6 +461,8 @@ func (c *ScoreboardController) GetPublic(ctx *gin.Context) {
|
||||
"secondaryColor": s.SecondaryColor,
|
||||
"homeScore": s.HomeScore,
|
||||
"awayScore": s.AwayScore,
|
||||
"homeFouls": s.HomeFouls,
|
||||
"awayFouls": s.AwayFouls,
|
||||
"halfLength": s.HalfLength,
|
||||
"theme": s.Theme,
|
||||
"external_match_id": s.ExternalMatchID,
|
||||
@@ -491,6 +507,8 @@ func (c *ScoreboardController) PutAdmin(ctx *gin.Context) {
|
||||
SecondaryColor *string `json:"secondaryColor"`
|
||||
HomeScore *int `json:"homeScore"`
|
||||
AwayScore *int `json:"awayScore"`
|
||||
HomeFouls *int `json:"homeFouls"`
|
||||
AwayFouls *int `json:"awayFouls"`
|
||||
HalfLength *int `json:"halfLength"`
|
||||
Theme *string `json:"theme"`
|
||||
ExternalMatchID *string `json:"externalMatchId"`
|
||||
@@ -523,6 +541,10 @@ func (c *ScoreboardController) PutAdmin(ctx *gin.Context) {
|
||||
if payload.SecondaryColor != nil { s.SecondaryColor = *payload.SecondaryColor }
|
||||
if payload.HomeScore != nil { s.HomeScore = *payload.HomeScore }
|
||||
if payload.AwayScore != nil { s.AwayScore = *payload.AwayScore }
|
||||
// Clamp fouls 0..5
|
||||
clamp := func(v int) int { if v < 0 { return 0 }; if v > 5 { return 5 }; return v }
|
||||
if payload.HomeFouls != nil { s.HomeFouls = clamp(*payload.HomeFouls) }
|
||||
if payload.AwayFouls != nil { s.AwayFouls = clamp(*payload.AwayFouls) }
|
||||
if payload.HalfLength != nil { s.HalfLength = *payload.HalfLength }
|
||||
if payload.Theme != nil { s.Theme = *payload.Theme }
|
||||
if payload.ExternalMatchID != nil { s.ExternalMatchID = *payload.ExternalMatchID }
|
||||
|
||||
@@ -9,7 +9,8 @@ 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
|
||||
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"`
|
||||
|
||||
@@ -33,6 +33,9 @@ type ScoreboardState struct {
|
||||
// QR overlay schedule settings
|
||||
QRShowEveryMinutes int `json:"qr_show_every_minutes"`
|
||||
QRShowDurationSeconds int `json:"qr_show_duration_seconds"`
|
||||
// Team fouls (0..5 display dots)
|
||||
HomeFouls int `json:"home_fouls"`
|
||||
AwayFouls int `json:"away_fouls"`
|
||||
}
|
||||
|
||||
func (ScoreboardState) TableName() string { return "scoreboard_states" }
|
||||
|
||||
Reference in New Issue
Block a user