mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
dev day #90 🥳
This commit is contained in:
@@ -33,16 +33,93 @@ func (cc *ContactController) GetContactMessages(c *gin.Context) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
|
||||
return
|
||||
}
|
||||
|
||||
// Pagination
|
||||
page, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("page", "1")))
|
||||
limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "50")))
|
||||
if page <= 0 { page = 1 }
|
||||
if limit <= 0 || limit > 200 { limit = 50 }
|
||||
items, total, err := models.GetContactMessages(cc.DB, page, limit)
|
||||
if err != nil {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if limit <= 0 || limit > 200 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
// Filters
|
||||
search := strings.TrimSpace(c.DefaultQuery("search", ""))
|
||||
isReadParam := strings.TrimSpace(c.DefaultQuery("isRead", ""))
|
||||
var isRead *bool
|
||||
if isReadParam != "" {
|
||||
v := strings.ToLower(isReadParam)
|
||||
if v == "true" || v == "1" {
|
||||
t := true
|
||||
isRead = &t
|
||||
} else if v == "false" || v == "0" {
|
||||
f := false
|
||||
isRead = &f
|
||||
}
|
||||
}
|
||||
|
||||
// Sorting (map UI fields to DB columns)
|
||||
sortBy := strings.TrimSpace(c.DefaultQuery("sortBy", "createdAt"))
|
||||
sortOrder := strings.ToLower(strings.TrimSpace(c.DefaultQuery("sortOrder", "desc")))
|
||||
if sortOrder != "asc" {
|
||||
sortOrder = "desc"
|
||||
}
|
||||
sortField := "created_at"
|
||||
switch sortBy {
|
||||
case "name":
|
||||
sortField = "name"
|
||||
case "email":
|
||||
sortField = "email"
|
||||
case "subject":
|
||||
sortField = "subject"
|
||||
case "createdAt", "created_at":
|
||||
sortField = "created_at"
|
||||
}
|
||||
|
||||
// Build query
|
||||
q := cc.DB.Model(&models.ContactMessage{})
|
||||
if search != "" {
|
||||
s := "%" + strings.ToLower(search) + "%"
|
||||
q = q.Where("LOWER(name) LIKE ? OR LOWER(email) LIKE ? OR LOWER(subject) LIKE ? OR LOWER(message) LIKE ?", s, s, s, s)
|
||||
}
|
||||
if isRead != nil {
|
||||
q = q.Where("is_read = ?", *isRead)
|
||||
}
|
||||
|
||||
// Count total
|
||||
var total int64
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch messages"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"items": items, "total": total, "page": page, "page_size": limit})
|
||||
|
||||
// Fetch page
|
||||
var items []models.ContactMessage
|
||||
offset := (page - 1) * limit
|
||||
if err := q.Order(sortField+" "+sortOrder).Offset(offset).Limit(limit).Find(&items).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch messages"})
|
||||
return
|
||||
}
|
||||
|
||||
// Compute pages (ceil)
|
||||
pages := 1
|
||||
if limit > 0 {
|
||||
pages = (int(total) + limit - 1) / limit
|
||||
if pages == 0 {
|
||||
pages = 1
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": items,
|
||||
"pagination": gin.H{
|
||||
"total": total,
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
"pages": pages,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// MarkMessageAsRead marks a message as read (admin)
|
||||
@@ -75,19 +152,36 @@ func (cc *ContactController) recalcNewsletterAutomationEnabled() {
|
||||
_ = cc.DB.Model(&models.NewsletterSubscription{}).Where("is_active = ?", true).Count(&active).Error
|
||||
enabled := active > 0
|
||||
|
||||
// Persist flag
|
||||
// Persist flag and, when enabling for the first time, ensure weekly digest is active with sane defaults
|
||||
var s models.Settings
|
||||
_ = cc.DB.First(&s).Error
|
||||
if s.ID == 0 {
|
||||
s = models.Settings{}
|
||||
}
|
||||
changed := false
|
||||
if s.NewsletterEnabled != enabled {
|
||||
s.NewsletterEnabled = enabled
|
||||
if s.ID == 0 {
|
||||
_ = cc.DB.Create(&s).Error
|
||||
} else {
|
||||
_ = cc.DB.Save(&s).Error
|
||||
changed = true
|
||||
}
|
||||
if enabled {
|
||||
// Auto-activate weekly digest and preset schedule if not configured
|
||||
if !s.EnableWeekly {
|
||||
s.EnableWeekly = true
|
||||
changed = true
|
||||
}
|
||||
if strings.TrimSpace(s.NewsletterWeeklyDay) == "" {
|
||||
s.NewsletterWeeklyDay = "sun"
|
||||
changed = true
|
||||
}
|
||||
if s.NewsletterWeeklyHour < 0 || s.NewsletterWeeklyHour > 23 {
|
||||
s.NewsletterWeeklyHour = 9
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if s.ID == 0 {
|
||||
_ = cc.DB.Create(&s).Error
|
||||
} else if changed {
|
||||
_ = cc.DB.Save(&s).Error
|
||||
}
|
||||
|
||||
// Update runtime
|
||||
@@ -736,6 +830,8 @@ func (cc *ContactController) GetNewsletterStatus(c *gin.Context) {
|
||||
var total, active int64
|
||||
cc.DB.Model(&models.NewsletterSubscription{}).Count(&total)
|
||||
cc.DB.Model(&models.NewsletterSubscription{}).Where("is_active = ?", true).Count(&active)
|
||||
var s models.Settings
|
||||
_ = cc.DB.First(&s).Error
|
||||
var subs []models.NewsletterSubscription
|
||||
_ = cc.DB.Where("is_active = ?", true).Limit(20).Find(&subs).Error
|
||||
sample := make([]string, 0, len(subs))
|
||||
@@ -751,6 +847,31 @@ func (cc *ContactController) GetNewsletterStatus(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
next := time.Now().Add(interval)
|
||||
// Compute next scheduled weekly time (exact), using settings (default Sun 09:00)
|
||||
weeklyDay := strings.ToLower(strings.TrimSpace(s.NewsletterWeeklyDay))
|
||||
if weeklyDay == "" { weeklyDay = "sun" }
|
||||
weeklyHour := s.NewsletterWeeklyHour
|
||||
if weeklyHour < 0 || weeklyHour > 23 { weeklyHour = 9 }
|
||||
// find next occurrence
|
||||
now := time.Now()
|
||||
target := time.Date(now.Year(), now.Month(), now.Day(), weeklyHour, 0, 0, 0, now.Location())
|
||||
toWD := func(d string) time.Weekday {
|
||||
switch d {
|
||||
case "mon": return time.Monday
|
||||
case "tue": return time.Tuesday
|
||||
case "wed": return time.Wednesday
|
||||
case "thu": return time.Thursday
|
||||
case "fri": return time.Friday
|
||||
case "sat": return time.Saturday
|
||||
default: return time.Sunday
|
||||
}
|
||||
}
|
||||
for i := 0; i < 8; i++ {
|
||||
if target.Weekday() == toWD(weeklyDay) && target.After(now) {
|
||||
break
|
||||
}
|
||||
target = target.Add(24 * time.Hour)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"total_subscribers": total,
|
||||
"active_subscribers": active,
|
||||
@@ -758,6 +879,16 @@ func (cc *ContactController) GetNewsletterStatus(c *gin.Context) {
|
||||
"interval_minutes": int(interval.Minutes()),
|
||||
"next_approximate": next,
|
||||
"newsletter_enabled": config.AppConfig != nil && config.AppConfig.NewsletterEnabled,
|
||||
// Scheduling detail
|
||||
"weekly_enabled": s.EnableWeekly,
|
||||
"weekly_day": weeklyDay,
|
||||
"weekly_hour": weeklyHour,
|
||||
"weekly_next_scheduled": target,
|
||||
"matches_enabled": s.EnableMatchReminders,
|
||||
"reminder_lead_hours": s.NewsletterReminderLeadHours,
|
||||
"results_enabled": s.EnableResults,
|
||||
"quiet_start": s.NewsletterQuietStart,
|
||||
"quiet_end": s.NewsletterQuietEnd,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user